diff --git a/.github/workflows/chart-docs.yml b/.github/workflows/chart-docs.yml new file mode 100644 index 0000000..7164419 --- /dev/null +++ b/.github/workflows/chart-docs.yml @@ -0,0 +1,30 @@ +name: Generate Chart README + +on: + pull_request: + push: + branches: + - main + workflow_dispatch: + +jobs: + chart-docs: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version: stable + + - name: Install helm-docs + run: go install github.com/norwoodj/helm-docs/cmd/helm-docs@v1.14.2 + + - name: Generate chart docs + run: | + "$(go env GOPATH)/bin/helm-docs" -x --chart-search-root . --chart-to-generate chart + + - name: Verify chart docs are current + run: git diff --exit-code chart/README.md diff --git a/.github/workflows/chart-test-matrix.yml b/.github/workflows/chart-test-matrix.yml new file mode 100644 index 0000000..f7c450e --- /dev/null +++ b/.github/workflows/chart-test-matrix.yml @@ -0,0 +1,44 @@ +name: Chart Test Matrix + +on: + pull_request: + push: + branches: + - main + workflow_dispatch: + +jobs: + chart-smoke-test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Set up Helm + uses: azure/setup-helm@v4 + + - name: Smoke-test chart + run: sh scripts/validate-chart.sh chart + + chart-unit-test-matrix: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + suite: + - database + - search + - init + - negative + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Set up Helm + uses: azure/setup-helm@v4 + + - name: Install helm-unittest + run: helm plugin install https://github.com/helm-unittest/helm-unittest.git --verify=false + + - name: Run chart matrix suite + run: helm unittest --strict -f "tests/${{ matrix.suite }}_test.yaml" chart diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5337f71..6566691 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,3 +1,5 @@ +name: Release + on: push: tags: @@ -17,25 +19,35 @@ jobs: token: "${{ secrets.PAT_ACTIONS_WORKFLOWS }}" fetch-depth: 0 - - name: update placeholder versions - working-directory: ./chart - run: | - apk add -q curl jq envsubst + - name: add prerequisites + run: apk add -q curl jq envsubst + - name: prepare release chart + run: | export APP_VERSION=$(curl -s https://account.fusionauth.io/api/version | jq -r '.versions[]' | sort -V | tail -n 1) export CHART_VERSION="${GITHUB_REF##*/}" + export RELEASE_CHART=/tmp/fusionauth-chart echo "Chart: $CHART_VERSION" echo "App: $APP_VERSION" + rm -rf "$RELEASE_CHART" + cp -R chart "$RELEASE_CHART" + for file in Chart.yaml values.yaml README.md examples/minikube/values.yaml do - tmpfile=$(mktemp) - cat "$file" > "$tmpfile" - envsubst < "$tmpfile" > "$file" + sed -i \ + -e "s/0\.0\.0-chart-dev/${CHART_VERSION}/g" \ + -e "s/0\.0\.0-app-dev/${APP_VERSION}/g" \ + "$RELEASE_CHART/$file" done echo "APP_VERSION=$APP_VERSION" >> $GITHUB_ENV + echo "CHART_VERSION=$CHART_VERSION" >> $GITHUB_ENV + echo "RELEASE_CHART=$RELEASE_CHART" >> $GITHUB_ENV + + - name: Smoke-test release chart + run: sh scripts/validate-chart.sh "$RELEASE_CHART" - name: configure git run: | @@ -59,7 +71,7 @@ jobs: CR_PAGES_BRANCH: main CR_RELEASE_NAME_TEMPLATE: "{{ .Version }}" run: | - cr package chart + cr package "$RELEASE_CHART" cr upload --release-notes-file /tmp/release-notes.md cr index --push ARCHIVE=$(ls ${CR_PACKAGE_PATH}) @@ -82,8 +94,8 @@ jobs: helm repo add fusionauth ${CHARTS_REPO} helm install fusionauth fusionauth/fusionauth \ --set database.host=host \ - --set database.user=user \ - --set database.password=password \ + --set database.dbUser.username=user \ + --set database.dbUser.password=password \ --set search.host=host - name: Verify the chart version diff --git a/README.md b/README.md index 3fe9091..a773895 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,49 @@ Beginning with 1.57.1, the Helm chart version is the same as the FusionAuth app We'll typically release any changes to the chart alongside new FusionAuth app versions. Changes will be called out in the release notes. If changes must be made to the chart outside of the FusionAuth app release cycle, we'll indicate that with a SemVer pre-release tag. For example, `1.57.1-1` would indicate the 1st revision of the chart after the `1.57.1` release, before the next FusionAuth app release. +## Testing Changes + +Install the Helm unit test plugin: + +```sh +helm plugin install https://github.com/helm-unittest/helm-unittest.git --verify=false +``` + +Run the chart test matrix locally: + +```sh +helm unittest --strict chart +sh scripts/validate-chart.sh chart +``` + +Changes to the chart should have corresponding tests, and the tests must pass prior to release. + +## Updating Chart Documentation + +The chart README is generated from README.md.gotmpl and `helm-docs`. Do not manually update `chart/README.md`. Update the template and regenerate it. + +Install `helm-docs` with homebrew: + +```sh +brew install norwoodj/tap/helm-docs +``` + +Install `helm-docs` with `go install`: + +```sh +go install github.com/norwoodj/helm-docs/cmd/helm-docs@v1.14.2 +``` + +Regenerate the chart README after changing `chart/values.yaml`, `chart/Chart.yaml`, or `chart/README.md.gotmpl`: + +```sh +helm-docs -x --chart-search-root . --chart-to-generate chart +``` + ## Releasing the Chart +Make sure you've run tests and generated docs before releasing. The release could fail if these are not done. + Release the chart by pushing a new tag. ``` diff --git a/chart/.helmignore b/chart/.helmignore index 50af031..f85b0ee 100644 --- a/chart/.helmignore +++ b/chart/.helmignore @@ -20,3 +20,4 @@ .idea/ *.tmproj .vscode/ +tests/ diff --git a/chart/Chart.yaml b/chart/Chart.yaml index 882619e..8621fb9 100644 --- a/chart/Chart.yaml +++ b/chart/Chart.yaml @@ -1,6 +1,8 @@ apiVersion: v2 name: fusionauth description: Helm chart for FusionAuth +icon: https://fusionauth.io/img/favicon.png type: application -version: ${CHART_VERSION} -appVersion: ${APP_VERSION} +kubeVersion: ">=1.23.0-0" +version: 0.0.0-chart-dev +appVersion: 0.0.0-app-dev diff --git a/chart/README.md b/chart/README.md index af83849..cb08d6a 100644 --- a/chart/README.md +++ b/chart/README.md @@ -4,22 +4,164 @@ [FusionAuth](https://fusionauth.io/) is a modern platform for Customer Identity and Access Management (CIAM). FusionAuth provides APIs and a responsive web user interface to support login, registration, localized email, multi-factor authentication, reporting, and much more. - -## Important Upgrade Info - -* **In `1.57.1` and later, the chart version now matches the FusionAuth app version.** - - ⚠️ You can (and probably should) override the `image.tag` field in `values.yaml` to pin the desired version of the FusionAuth application. This ensures that upgrading the helm chart doesn't unexpectedly upgrade the FusionAuth version. - -* **In `1.0.0` and later, the FusionAuth app version will now default to the latest available at the time of the chart's release.** Release notes will indicate the FusionAuth version included in the chart. - - ⚠️ You can (and probably should) override the `image.tag` field in `values.yaml` to pin the desired version of the FusionAuth application. This ensures that upgrading the helm chart doesn't unexpectedly upgrade the FusionAuth version. - - -* **In `0.8.0`, the `environment` value is now an array instead of an object.** Make sure to reformat your values when you update. - -* **In `0.4.0`, the external postgresql and elasticsearch charts were dropped.** You will need to maintain those dependencies on your own. - +## Important Changes + +### 1.67.0 + +⚠️ This release contains several breaking changes, as well as recommended changes. +Review your values file carefully against these notes! + +#### Breaking Changes + +- **The minimum supported Kubernetes version is now 1.23.0.** This removes support + for long-deprecated beta APIs for `HorizontalPodAutoscaler`, `Ingress`, and + `PodDisruptionBudget`. + +- **`service.spec` has been removed from the chart** to eliminate the risk of + overwriting valid service configurations. If you used `service.spec` in a way + that is not supported by the standard chart values, please open an issue + describing your use case. + +- **`service.type` no longer supports `ExternalName`.** `ExternalName` support + should not be required in this chart. If you used `ExternalName`, please open + an issue describing your use case. + +- **`environment` can no longer override variables managed by chart values.** + If you have set any of the following variables in the `environment` section, + you must remove them and use the corresponding chart values instead. + | Env Var | Chart Value | + | --- | --- | + | `DATABASE_USERNAME` | `database.dbUser.username` | + | `DATABASE_PASSWORD` | `database.dbUser.password` | + | `DATABASE_ROOT_USERNAME` | `database.rootUser.username` | + | `DATABASE_ROOT_PASSWORD` | `database.rootUser.password` | + | `DATABASE_URL` | `database.url` | + | `FUSIONAUTH_APP_MEMORY` | `fusionauth.app.memory` | + | `FUSIONAUTH_APP_RUNTIME_MODE` | `fusionauth.app.runtimeMode` | + | `FUSIONAUTH_APP_SILENT_MODE` | `fusionauth.app.silentMode` | + | `FUSIONAUTH_APP_KICKSTART_FILE` | `kickstart.file` | + | `SEARCH_TYPE` | `search.engine` | + | `SEARCH_SERVERS` | `search.host`
`search.protocol`
`search.port` | + | `SEARCH_USERNAME` | `search.basicAuth.username` | + | `SEARCH_PASSWORD` | `search.basicAuth.password` | + +- **Chart-managed database secrets have changed.** + - If you are using `existingSecret` to store passwords, this does not affect you. + - If you are not using `existingSecret`, we recommend that you do, as storing passwords + in clear text in the values file is not secure. If you continue to store passwords in + the values file, you will be impacted by these changes. + - The previous secret `-credentials` is no longer created or used by + the chart. Instead, the chart creates two new secrets: + - `-db-credentials` contains the password from `database.dbUser.password`. + - `-db-root-credentials` contains the password from `database.rootUser.password`. + +#### Recommended Migrations + +There are additional changes to the values, but these changes include compatibility +shims to give you time to migrate. It's recommended to migrate to the new values +as soon as possible, as the compatibility shims will be removed in a future chart release. + +- **Values for `database` credentials have been updated.** + + If you are using `existingSecret` to store the database passwords (recommended): + + ```yaml + # Old values + database: + user: fusionauth # name of the database user + existingSecret: fusionauth-db-creds # name of the k8s Secret + root: + user: postgres # name of the root user + + # New values + database: + dbUser: + username: fusionauth # name of the database user + existingSecret: + enabled: true + name: fusionauth-db-creds # name of the k8s Secret + passwordKey: password # name of the key that stores the password + rootUser: + username: postgres # name of the root user + existingSecret: + enabled: true + name: fusionauth-root-creds # name of the k8s Secret + passwordKey: password # name of the key that stores the root password + ``` + + If you are storing the database passwords in clear text (NOT recommended): + + ```yaml + # Old values + database: + user: fusionauth + password: password + root: + user: postgres + password: password + + # New values + database: + dbUser: + username: fusionauth + password: password + rootUser: + username: postgres + password: password + ``` + + 📝 Whether you use the new shape or not, if you are not using `existingSecret`, + the chart will now create separate Secrets for the database user and the root + user, instead of putting both passwords into a single secret. + +- `initContainers.waitForEs` renamed to `initContainers.waitForSearch` + +- Values for `search` credentials have changed. + - A `basicAuth` key was added to prepare for support of other credential types in the future. + - `search.basicAuth` now supports `existingSecret`. + + ```yaml + # Old values + search: + user: username # name of the search user + password: password # password for the search user + + # New values + search: + basicAuth: + enabled: true + username: username + password: password + + # New values with existingSecret + search: + basicAuth: + existingSecret: + enabled: true + name: fusionauth-search-creds + userKey: username + passwordKey: password + ``` + +### 1.57.1 + +- **The chart version now matches the FusionAuth app version.** + + ⚠️ You can (and probably should) override the `image.tag` field in `values.yaml` to pin the desired version of the FusionAuth application. This ensures that upgrading the helm chart doesn't unexpectedly upgrade the FusionAuth version. + +### 1.0.0 + +- **The FusionAuth app version will now default to the latest available at the time of the chart's release.** Release notes will indicate the FusionAuth version included in the chart. + + ⚠️ You can (and probably should) override the `image.tag` field in `values.yaml` to pin the desired version of the FusionAuth application. This ensures that upgrading the helm chart doesn't unexpectedly upgrade the FusionAuth version. + +### 0.8.0 + +- **The `environment` value is now an array instead of an object.** Make sure to reformat your values when you update. + +### 0.4.0 + +- **The external postgresql and elasticsearch charts were dropped.** You will need to maintain those dependencies on your own. ## Installing the Chart @@ -27,8 +169,8 @@ You can read the official instructions, including install steps for AWS, GCP, an ### Prerequisites -* PostgreSQL or MySQL database -* ElasticSearch or OpenSearch instance (optional) +- PostgreSQL or MySQL database +- ElasticSearch or OpenSearch instance (optional) ⚠️ Though an ElasticSearch or OpenSearch instance is optional, it is strongly recommended for most use cases. @@ -40,23 +182,24 @@ To install the chart with the release name `fusionauth`: helm repo add fusionauth https://fusionauth.github.io/charts helm install fusionauth fusionauth/fusionauth \ --set database.host=[database host] \ - --set database.user=[database username] \ - --set database.password=[database password] \ + --set database.dbUser.username=[database username] \ + --set database.dbUser.password=[database password] \ --set search.host=[elasticsearch host] ``` - ## Setting Up a Test Deployment This will install FusionAuth and its prerequisites in a single kubernetes namespace, with a configuration suitable for evaluation and testing. **This configuration is not suitable for production.** Create and switch to the test namespace. + ```shell kubectl create namespace fusionauth-test kubectl config set-context --current --namespace=fusionauth-test ``` ### Install PostgreSQL + ```shell helm install postgres oci://registry-1.docker.io/bitnamicharts/postgresql ``` @@ -64,6 +207,7 @@ helm install postgres oci://registry-1.docker.io/bitnamicharts/postgresql ### Install Opensearch Opensearch is optional, but highly recommended. See the note below. + ```shell helm repo add opensearch https://opensearch-project.github.io/helm-charts/ helm install opensearch opensearch/opensearch \ @@ -74,13 +218,14 @@ helm install opensearch opensearch/opensearch \ ### Install FusionAuth Wait for the Postgres and Opensearch pods to be ready, then install FusionAuth. + ```shell export FA_PSQL_PASS=$(kubectl get secret postgres-postgresql -o jsonpath="{.data.postgres-password}" | base64 -d) helm repo add fusionauth https://fusionauth.github.io/charts helm install fusionauth fusionauth/fusionauth \ --set database.host=postgres-postgresql \ ---set database.user=fusionauth \ ---set database.password=$FA_PSQL_PASS \ +--set database.dbUser.username=fusionauth \ +--set database.dbUser.password=$FA_PSQL_PASS \ --set search.host=opensearch-cluster-master ``` @@ -89,6 +234,7 @@ helm install fusionauth fusionauth/fusionauth \ ### Connect to FusionAuth Create a port forward to connect to the FusionAuth app. + ```shell kubectl port-forward svc/fusionauth 9011:9011 ``` @@ -97,479 +243,140 @@ You should now be able to connect to the FusionAuth application at http://localh 📝 You may wish to set up an ingress instead of using a port forward. See the table below for how to configure the FusionAuth chart values to add an ingress. - ## Chart Values - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KeyTypeDefaultDescription
affinityobject{}Configure affinity rules for the fusionauth Deployment.
annotationsobject{}Define annotations for fusionauth Deployment.
app.memorystring"256M"Configures the amount of memory to allocate to the Java VM (sets FUSIONAUTH_APP_MEMORY).
app.runtimeModestring"development"Configures runtime mode (sets FUSIONAUTH_APP_RUNTIME_MODE). Must be development or production.
app.silentModeboolfalseConfigures silent mode (sets FUSIONAUTH_APP_SILENT_MODE). Must be true or false.
autoscaling.enabledboolfalseEnable Horizontal Pod Autoscaling. See the values file for more HPA parameters.
autoscaling.minReplicasint2Minimum number of running instances when HPA is enabled. Ignored when autoscaling.enabled is false.
autoscaling.maxReplicasint5Maximum number of running instances when HPA is enabled. Ignored when autoscaling.enabled is false.
autoscaling.targetCPUint50CPU use % threshold to trigger a HPA scale up. Ignored when autoscaling.enabled is false.
database.existingSecretstring""The name of an existing Kubernetes Secret that contains the database passwords.
database.hoststring""Hostname or IP address of the fusionauth database.
database.namestring"fusionauth"Name of the fusionauth database.
database.passwordstring""Database password for fusionauth to use in normal operation - not required if database.existingSecret is configured.
database.portint5432Port used by the fusionauth database.
database.protocolstring"postgresql"Should either be postgresql or mysql. Protocol for jdbc connection to database.
database.root.passwordstring""Database password for fusionauth to use during initial bootstrap - not required if database.existingSecret is configured.
database.root.userstring""Database username for fusionauth to use during initial bootstrap - not required if you have manually bootstrapped your database.
database.tlsboolfalseConfigures whether or not to use tls when connecting to the database.
database.tlsModestring"require"If tls is enabled, this configures the mode.
database.userstring""Database username for fusionauth to use in normal operation.
dnsConfigobject{}Define dnsConfig for fusionauth pods.
dnsPolicystring"ClusterFirst"Define dnsPolicy for fusionauth pods.
environmentlist[]Configure additional environment variables.
extraVolumeMountslist[]Define mount paths for extraVolumes.
extraContainerslist[]Create containers for the pods. Can be used for sidecars, ambassador, and adapter patterns.
extraInitContainerslist[]Add extra init containers. Can be used for setup or wait for other dependent services.
extraVolumeslist[]Define extra volumes to mount in the deployment.
fullnameOverridestring""Overrides full resource names.
image.pullPolicystring"IfNotPresent"Kubernetes image pullPolicy to use for fusionauth-app.
image.repositorystring"fusionauth/fusionauth-app"The image repository to use for fusionauth-app.
image.tagstring"${APP_VERSION}"The image tag to pull for fusionauth-app (this is the fusionauth-app version).
imagePullSecretslist[]Configures Kubernetes secrets to use for pulling images from private repositories.
ingress.annotationsobject{}Configure annotations to add to the ingress object.
ingress.enabledboolfalseEnables ingress creation for fusionauth.
ingress.extraPathslist[]Define path objects which will be inserted before regular paths. Can be useful for things like ALB Ingress Controller actions.
ingress.hostslist[]List of hostnames to configure the ingress with.
ingress.ingressClassNamestring""Specify the ingressClass to be used by the Ingress.
ingress.pathslist[]Paths to be used by the Ingress.
ingress.tlslist[]List of secrets used to configure TLS for the ingress.
initContainers.waitForDbbooltrueCreate an init container which waits for the database to be ready.
initContainers.waitForEsbooltrueCreate an init container which waits for elasticsearch to be ready.
initContainers.image.repositorystring"busybox"Image to use for initContainers docker image.
initContainers.image.tagstring"1.36.1"Tag to use for initContainers docker image.
initContainers.resourcesobject{}Resource requests and limits to use for initContainers.
kickstart.dataobject{}Fusionauth kickstart settings.
kickstart.enabledboolfalseEnable fusionauth kickstart settings.
lifecycleobject{}Define custom lifecycle settings for the deployment.
livenessProbeobject -
-livenessProbe:
-  httpGet:
-    path: /
-    port: http
-  failureThreshold: 3
-  periodSeconds: 30
-  timeoutSeconds: 5
Configures a livenessProbe to ensure fusionauth is running.
nameOverridestring""Overrides resource names.
nodeSelectorobject{}Define nodeSelector for kubernetes to use when scheduling fusionauth pods.
podAnnotationsobject{}Define annotations for fusionauth pods.
podDisruptionBudget.enabledboolfalseEnables creation of a PodDisruptionBudget.
podLabelsobject{}Define labels for fusionauth Deployment.
podSecurityContextobject{}Security context for the pod. Ref: Kubernetes docs.
readinessProbeobject -
-readinessProbe:
-  httpGet:
-    path: /
-    port: http
-  failureThreshold: 5
-  timeoutSeconds: 5
Configures a readinessProbe to ensure fusionauth is ready for requests.
replicaCountint1The number of fusionauth-app instances to run.
resourcesobject{}Define resource requests and limits for fusionauth-app.
search.enginestring"elasticsearch"Protocol to use when connecting to elasticsearch. Ignored when search.engine is NOT elasticsearch.
search.hoststring""Hostname or ip to use when connecting to elasticsearch. Ignored when search.engine is NOT elasticsearch.
search.portint9200Port to use when connecting to elasticsearch. Ignored when search.engine is NOT elasticsearch.
search.protocolstring"http"Protocol to use when connecting to elasticsearch. Ignored when search.engine is NOT elasticsearch.
securityContextobject{}Security context for the fusionauth container. Ref: Kubernetes docs.
service.annotationsobject{}Extra annotations to add to the service object.
service.portint9011Port for the Kubernetes service to expose.
service.specobject{}Any extra fields to add to the service object spec.
service.typestring"ClusterIP"Type of Kubernetes service to create.
serviceAccount.annotationsobject{}Extra annotations to add to the service account object.
serviceAccount.automountboolfalseAutomatically mount a service account's API credentials.
serviceAccount.createboolfalseIf set to true, service account will be created. Otherwise, the default serviceaccount will be used.
serviceAccount.namestring""The name of the service account to use. If not set and create is true, a name is generated using the fullname template.
startupProbeobject -
-startupProbe:
-  httpGet:
-    path: /
-    port: http
-  failureThreshold: 20
-  periodSeconds: 10
-  timeoutSeconds: 5
Configures a startupProbe to ensure fusionauth has finished starting up.
tolerationslist[]Define tolerations for kubernetes to use when scheduling fusionauth pods.
topologySpreadConstraintslist[]Define topologySpreadConstraints for kubernetes to use when scheduling fusionauth pods.
+| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | Define affinity for kubernetes to use when scheduling fusionauth pods. | +| annotations | object | `{}` | Define annotations for fusionauth deployment. | +| app | object | `{"memory":"256M","runtimeMode":"development","silentMode":false}` | Configures general settings for the fusionauth application | +| app.memory | string | `"256M"` | Configures the amount of memory Java can use | +| app.runtimeMode | string | `"development"` | Configures runtime mode for fusionauth. Should be 'development' or 'production' learn more about the difference here: https://fusionauth.io/docs/v1/tech/reference/configuration | +| app.silentMode | bool | `false` | Configures silent mode for fusionauth. Should be 'true' or 'false' learn more about silent mode here: https://fusionauth.io/docs/get-started/download-and-install/silent-mode silent-mode minimizes downtime during upgrades: https://fusionauth.io/docs/operate/deploy/upgrade#downtime-and-database-migrations | +| autoscaling | object | `{"enabled":false,"maxReplicas":5,"minReplicas":2,"targetCPU":50}` | Configures Horizontal Pod Autoscaling. If you enable autoscaling, you will need to also set resource requests for the corresponding targets. | +| autoscaling.enabled | bool | `false` | Enable Horizontal Pod Autoscaling. | +| autoscaling.maxReplicas | int | `5` | Maximum number of running instances when HPA is enabled. | +| autoscaling.minReplicas | int | `2` | Minimum number of running instances when HPA is enabled. | +| autoscaling.targetCPU | int | `50` | CPU use % threshold to trigger a HPA scale up. | +| database | object | `{"dbUser":{"existingSecret":{"enabled":false,"name":"","passwordKey":"password"},"password":"","username":""},"host":"","name":"fusionauth","port":5432,"protocol":"postgresql","rootUser":{"existingSecret":{"enabled":false,"name":"","passwordKey":"password"},"password":"","username":""},"tls":false,"tlsMode":"require","url":""}` | Configures the database connection for fusionauth | +| database.dbUser | object | `{"existingSecret":{"enabled":false,"name":"","passwordKey":"password"},"password":"","username":""}` | Database credentials for fusionauth to use in normal operation | +| database.dbUser.existingSecret | object | `{"enabled":false,"name":"","passwordKey":"password"}` | Configures an existing secret that contains the normal database user password. | +| database.dbUser.existingSecret.enabled | bool | `false` | Use an existing secret for the normal database user password. | +| database.dbUser.existingSecret.name | string | `""` | The name of an existing secret that contains the normal database user password. | +| database.dbUser.existingSecret.passwordKey | string | `"password"` | The key in the existing secret that contains the database password. | +| database.dbUser.password | string | `""` | Database password for fusionauth to use in normal operation. It is not recommended to set the password in clear text here. Use an existing secret instead. | +| database.dbUser.username | string | `""` | Database username for fusionauth to use in normal operation. | +| database.host | string | `""` | Hostname or ip of the database instance. Required by the wait-for-db init container even when database.url is set. | +| database.name | string | `"fusionauth"` | Name of the fusionauth database | +| database.port | int | `5432` | Port of the database instance. Required by the wait-for-db init container even when database.url is set. | +| database.protocol | string | `"postgresql"` | Protocol for jdbc connection to database [`postgresql|mysql`]. | +| database.rootUser | object | `{"existingSecret":{"enabled":false,"name":"","passwordKey":"password"},"password":"","username":""}` | Database credentials for fusionauth to use during initial bootstrap | +| database.rootUser.existingSecret | object | `{"enabled":false,"name":"","passwordKey":"password"}` | Configures an existing secret that contains the root database user password. | +| database.rootUser.existingSecret.enabled | bool | `false` | Use an existing secret for the root database user password. | +| database.rootUser.existingSecret.name | string | `""` | The name of an existing secret that contains the root database user password. | +| database.rootUser.existingSecret.passwordKey | string | `"password"` | The key in the existing secret that contains the root database password. | +| database.rootUser.password | string | `""` | Database password for fusionauth to use during initial bootstrap It is not recommended to set the password in clear text here. Use an existing secret instead. | +| database.rootUser.username | string | `""` | Database username for fusionauth to use during initial bootstrap | +| database.tls | bool | `false` | Configures whether or not to use tls when connecting to the database | +| database.tlsMode | string | `"require"` | If tls is enabled, this configures the mode | +| database.url | string | `""` | Optional full JDBC URL. When set, this value is used for DATABASE_URL instead of building it from protocol, host, port, name, tls, and tlsMode. | +| dnsConfig | object | `{}` | Define dnsConfig for fusionauth pods. | +| dnsPolicy | string | `"ClusterFirst"` | Define dnsPolicy for fusionauth pods. | +| environment | list | `[]` | Configure additional environment variables. Should only be used for things that are not explicitly set elsewhere in the chart. | +| extraContainers | list | `[]` | Add specs for additional containers if needed. | +| extraInitContainers | list | `[]` | Add specs for additional init containers if needed. | +| extraVolumeMounts | list | `[]` | Associate mountPath for each extraVolumes | +| extraVolumes | list | `[]` | Define extra Volumes. Allow to add existing claimName | +| fullnameOverride | string | `""` | Overrides full resource names | +| image | object | `{"pullPolicy":"IfNotPresent","repository":"docker.io/fusionauth/fusionauth-app","tag":"0.0.0-app-dev"}` | Configures the docker image to use for fusionauth-app | +| image.pullPolicy | string | `"IfNotPresent"` | Kubernetes image pullPolicy to use for fusionauth-app | +| image.repository | string | `"docker.io/fusionauth/fusionauth-app"` | The name of the docker repository for fusionauth-app | +| image.tag | string | `"0.0.0-app-dev"` | The docker tag to pull for fusionauth-app | +| imagePullSecrets | list | `[]` | Configures kubernetes secrets to use for pulling private images | +| ingress | object | `{"annotations":{},"enabled":false,"extraPaths":[],"hosts":[],"ingressClassName":null,"paths":[],"tls":[]}` | Configures ingress for FusionAuth. | +| ingress.annotations | object | `{}` | Configure annotations to add to the ingress object | +| ingress.enabled | bool | `false` | Enables ingress creation for fusionauth. | +| ingress.extraPaths | list | `[]` | Define complete path objects, will be inserted before regular paths. Can be useful for things like ALB Ingress Controller actions | +| ingress.hosts | list | `[]` | List of hostnames to configure the ingress with | +| ingress.ingressClassName | string/null | null | Specify the ingressClass to be used by the Ingress. The kubernetes.io/ingress.class annotation is deprecated as of networking.k8s.io/v1 or Kubernetes 1.22+. | +| ingress.paths | list | `[]` | Paths to be used by the Ingress. | +| ingress.tls | list | `[]` | List of secrets used to configure TLS for the ingress. | +| initContainers | object | `{"image":{"repository":"docker.io/library/busybox","tag":"1.36.1"},"resources":{},"waitForDb":true,"waitForSearch":true}` | Configures init containers for fusionauth pods. Init containers are used to wait for the database and search engine to be ready before starting fusionauth. | +| initContainers.image | object | `{"repository":"docker.io/library/busybox","tag":"1.36.1"}` | Configures the docker image to use for init containers. | +| initContainers.image.repository | string | `"docker.io/library/busybox"` | Docker image to use for initContainers. This image must contain `nc`, `wget` and a shell of some kind to do a simple loop. | +| initContainers.image.tag | string | `"1.36.1"` | Tag to use for initContainers docker image | +| initContainers.resources | object | `{}` | It is recommended to set these values when you understand FusionAuth's resource usage in your specific environment. | +| initContainers.waitForDb | bool | `true` | waits for the database to be ready. Setting this to `false` is not recommended. | +| initContainers.waitForSearch | bool | `true` | waits for the search engine to be ready. Setting this to `false` is not recommended. | +| kickstart | object | `{"data":{},"enabled":false,"file":"/kickstart/kickstart.json"}` | Configures kickstart for initial application setup | +| kickstart.data | object | `{}` | FusionAuth kickstart settings. | +| kickstart.enabled | bool | `false` | Enable kickstart for initial application setup. | +| kickstart.file | string | `"/kickstart/kickstart.json"` | File path FusionAuth should use for kickstart configuration. | +| lifecycle | object | `{}` | Define custom lifecycle settings for the deployment. | +| livenessProbe | object | `{"failureThreshold":3,"httpGet":{"path":"/","port":"http"},"periodSeconds":30,"timeoutSeconds":5}` | Configures a livenessProbe to ensure fusionauth is running | +| livenessProbe.failureThreshold | int | `3` | Failure threshold for the liveness probe. | +| livenessProbe.httpGet | object | `{"path":"/","port":"http"}` | Configures the liveness probe HTTP endpoint. | +| livenessProbe.httpGet.path | string | `"/"` | Path used for the liveness probe. | +| livenessProbe.httpGet.port | string | `"http"` | Port used for the liveness probe. | +| livenessProbe.periodSeconds | int | `30` | Period in seconds between liveness probe checks. | +| livenessProbe.timeoutSeconds | int | `5` | Timeout in seconds for the liveness probe. | +| nameOverride | string | `""` | Overrides resource names | +| nodeSelector | object | `{}` | Define nodeSelector for kubernetes to use when scheduling fusionauth pods. | +| podAnnotations | object | `{}` | Define annotations for fusionauth pods. | +| podDisruptionBudget | object | `{"enabled":false,"maxUnavailable":null,"minAvailable":null}` | Configures the PodDisruptionBudget for FusionAuth pods. | +| podDisruptionBudget.enabled | bool | `false` | Enables creation of a PodDisruptionBudget | +| podDisruptionBudget.maxUnavailable | int/string/null | null | Maximum number of unavailable pods. Cannot be used with minAvailable. Defaults to replicaCount - 1. | +| podDisruptionBudget.minAvailable | int/string/null | null | Minimum number of available pods. Cannot be used with maxUnavailable. | +| podLabels | object | `{}` | Define labels for fusionauth pods. | +| podSecurityContext | object | `{}` | Security context for the pod. Ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ | +| readinessProbe | object | `{"failureThreshold":5,"httpGet":{"path":"/","port":"http"},"timeoutSeconds":5}` | Configures a readinessProbe to ensure fusionauth is ready for requests | +| readinessProbe.failureThreshold | int | `5` | Failure threshold for the readiness probe. | +| readinessProbe.httpGet | object | `{"path":"/","port":"http"}` | Configures the readiness probe HTTP endpoint. | +| readinessProbe.httpGet.path | string | `"/"` | Path used for the readiness probe. | +| readinessProbe.httpGet.port | string | `"http"` | Port used for the readiness probe. | +| readinessProbe.timeoutSeconds | int | `5` | Timeout in seconds for the readiness probe. | +| replicaCount | int | `1` | The number of fusionauth-app instances to run | +| resources | object | `{}` | Define resource requests and limits for fusionauth-app. It is recommended to set these values when you understand FusionAuth's resource usage in your specific environment. | +| search | object | `{"basicAuth":{"enabled":false,"existingSecret":{"enabled":false,"name":"","passwordKey":"password","userKey":"username"},"password":"","username":""},"engine":"elasticsearch","host":"","port":9200,"protocol":"http"}` | Configures the search engine for fusionauth | +| search.basicAuth | object | `{"enabled":false,"existingSecret":{"enabled":false,"name":"","passwordKey":"password","userKey":"username"},"password":"","username":""}` | Configures elasticsearch basic auth credentials. Ignored when search.engine is NOT elasticsearch. | +| search.basicAuth.enabled | bool | `false` | Enables elasticsearch basic auth using inline username/password. Not required when search.basicAuth.existingSecret.enabled is true. | +| search.basicAuth.existingSecret | object | `{"enabled":false,"name":"","passwordKey":"password","userKey":"username"}` | Configures an existing secret that contains elasticsearch basic auth credentials. | +| search.basicAuth.existingSecret.enabled | bool | `false` | Use an existing secret for elasticsearch basic auth credentials. | +| search.basicAuth.existingSecret.name | string | `""` | The name of an existing secret that contains elasticsearch basic auth credentials. | +| search.basicAuth.existingSecret.passwordKey | string | `"password"` | The key in search.basicAuth.existingSecret.name that contains the elasticsearch password. | +| search.basicAuth.existingSecret.userKey | string | `"username"` | The key in search.basicAuth.existingSecret.name that contains the elasticsearch username. | +| search.basicAuth.password | string | `""` | Password to use with elasticsearch basic auth. Ignored when search.basicAuth.existingSecret.enabled is true. | +| search.basicAuth.username | string | `""` | Username to use with elasticsearch basic auth. Ignored when search.basicAuth.existingSecret.enabled is true. | +| search.engine | string | `"elasticsearch"` | Defines backend for fusionauth search capabilities. Valid values for engine are 'elasticsearch' or 'database'. | +| search.host | string | `""` | Hostname or ip to use when connecting to elasticsearch. Ignored when search.engine is NOT elasticsearch | +| search.port | int | `9200` | Port to use when connecting to elasticsearch. Ignored when search.engine is NOT elasticsearch | +| search.protocol | string | `"http"` | Protocol to use when connecting to elasticsearch. Ignored when search.engine is NOT elasticsearch | +| securityContext | object | `{}` | Security context for the fusionauth container. Ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ | +| service | object | `{"annotations":{},"port":9011,"type":"ClusterIP"}` | Configures the Kubernetes service for FusionAuth. | +| service.annotations | object | `{}` | Extra annotations to add to service object | +| service.port | int | `9011` | Port for the Kubernetes service to expose | +| service.type | string | `"ClusterIP"` | Type of Kubernetes service to create | +| serviceAccount | object | `{"annotations":{},"automount":true,"create":false,"name":""}` | Configures the Kubernetes service account for FusionAuth pods. | +| serviceAccount.annotations | object | `{}` | Annotations to add to the service account | +| serviceAccount.automount | bool | `true` | Automatically mount a ServiceAccount's API credentials? | +| serviceAccount.create | bool | `false` | Specifies whether a service account should be created | +| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the fullname template | +| serviceMonitor | object | `{"annotations":{},"basicAuth":{},"enabled":false,"interval":null,"labels":{},"namespaceSelector":{},"path":"/api/prometheus/metrics","relabelings":[],"scrapeTimeout":null}` | Configures a Prometheus operator ServiceMonitor custom resource Ref: https://fusionauth.io/docs/v1/tech/tutorials/prometheus | +| serviceMonitor.annotations | object | `{}` | Annotations to add to the ServiceMonitor object. | +| serviceMonitor.basicAuth | object | `{}` | Configures basic auth for prometheus, this is required for the serviceMonitor to work with FusionAuth because metrics sit behind an authenticated endpoint | +| serviceMonitor.enabled | bool | `false` | Enables creation of a ServiceMonitor | +| serviceMonitor.interval | string/null | null | Interval at which Prometheus should scrape metrics. | +| serviceMonitor.labels | object | `{}` | Labels to add to the ServiceMonitor object. | +| serviceMonitor.namespaceSelector | object | `{}` | Namespace selector for the ServiceMonitor. | +| serviceMonitor.path | string | `"/api/prometheus/metrics"` | Configures path to metrics, defaults to FusionAuth's prometheus metrics API endpoint | +| serviceMonitor.relabelings | list | `[]` | Relabeling rules for the ServiceMonitor endpoint. | +| serviceMonitor.scrapeTimeout | string/null | null | Timeout for Prometheus metric scrapes. | +| startupProbe | object | `{"failureThreshold":20,"httpGet":{"path":"/","port":"http"},"periodSeconds":10,"timeoutSeconds":5}` | Configures a startupProbe to ensure fusionauth has finished starting up | +| startupProbe.failureThreshold | int | `20` | Failure threshold for the startup probe. | +| startupProbe.httpGet | object | `{"path":"/","port":"http"}` | Configures the startup probe HTTP endpoint. | +| startupProbe.httpGet.path | string | `"/"` | Path used for the startup probe. | +| startupProbe.httpGet.port | string | `"http"` | Port used for the startup probe. | +| startupProbe.periodSeconds | int | `10` | Period in seconds between startup probe checks. | +| startupProbe.timeoutSeconds | int | `5` | Timeout in seconds for the startup probe. | +| tolerations | list | `[]` | Define tolerations for kubernetes to use when scheduling fusionauth pods. | +| topologySpreadConstraints | list | `[]` | Define topologySpreadConstraints for kubernetes to use when scheduling fusionauth pods. | diff --git a/chart/README.md.gotmpl b/chart/README.md.gotmpl new file mode 100644 index 0000000..69c3ce5 --- /dev/null +++ b/chart/README.md.gotmpl @@ -0,0 +1,248 @@ +# FusionAuth Helm Chart + +![Build Status](https://github.com/FusionAuth/charts/actions/workflows/release.yml/badge.svg) + +[FusionAuth](https://fusionauth.io/) is a modern platform for Customer Identity and Access Management (CIAM). FusionAuth provides APIs and a responsive web user interface to support login, registration, localized email, multi-factor authentication, reporting, and much more. + +## Important Changes + +### 1.67.0 + +⚠️ This release contains several breaking changes, as well as recommended changes. +Review your values file carefully against these notes! + +#### Breaking Changes + +- **The minimum supported Kubernetes version is now 1.23.0.** This removes support + for long-deprecated beta APIs for `HorizontalPodAutoscaler`, `Ingress`, and + `PodDisruptionBudget`. + +- **`service.spec` has been removed from the chart** to eliminate the risk of + overwriting valid service configurations. If you used `service.spec` in a way + that is not supported by the standard chart values, please open an issue + describing your use case. + +- **`service.type` no longer supports `ExternalName`.** `ExternalName` support + should not be required in this chart. If you used `ExternalName`, please open + an issue describing your use case. + +- **`environment` can no longer override variables managed by chart values.** + If you have set any of the following variables in the `environment` section, + you must remove them and use the corresponding chart values instead. + | Env Var | Chart Value | + | --- | --- | + | `DATABASE_USERNAME` | `database.dbUser.username` | + | `DATABASE_PASSWORD` | `database.dbUser.password` | + | `DATABASE_ROOT_USERNAME` | `database.rootUser.username` | + | `DATABASE_ROOT_PASSWORD` | `database.rootUser.password` | + | `DATABASE_URL` | `database.url` | + | `FUSIONAUTH_APP_MEMORY` | `fusionauth.app.memory` | + | `FUSIONAUTH_APP_RUNTIME_MODE` | `fusionauth.app.runtimeMode` | + | `FUSIONAUTH_APP_SILENT_MODE` | `fusionauth.app.silentMode` | + | `FUSIONAUTH_APP_KICKSTART_FILE` | `kickstart.file` | + | `SEARCH_TYPE` | `search.engine` | + | `SEARCH_SERVERS` | `search.host`
`search.protocol`
`search.port` | + | `SEARCH_USERNAME` | `search.basicAuth.username` | + | `SEARCH_PASSWORD` | `search.basicAuth.password` | + +- **Chart-managed database secrets have changed.** + - If you are using `existingSecret` to store passwords, this does not affect you. + - If you are not using `existingSecret`, we recommend that you do, as storing passwords + in clear text in the values file is not secure. If you continue to store passwords in + the values file, you will be impacted by these changes. + - The previous secret `-credentials` is no longer created or used by + the chart. Instead, the chart creates two new secrets: + - `-db-credentials` contains the password from `database.dbUser.password`. + - `-db-root-credentials` contains the password from `database.rootUser.password`. + +#### Recommended Migrations + +There are additional changes to the values, but these changes include compatibility +shims to give you time to migrate. It's recommended to migrate to the new values +as soon as possible, as the compatibility shims will be removed in a future chart release. + +- **Values for `database` credentials have been updated.** + + If you are using `existingSecret` to store the database passwords (recommended): + + ```yaml + # Old values + database: + user: fusionauth # name of the database user + existingSecret: fusionauth-db-creds # name of the k8s Secret + root: + user: postgres # name of the root user + + # New values + database: + dbUser: + username: fusionauth # name of the database user + existingSecret: + enabled: true + name: fusionauth-db-creds # name of the k8s Secret + passwordKey: password # name of the key that stores the password + rootUser: + username: postgres # name of the root user + existingSecret: + enabled: true + name: fusionauth-root-creds # name of the k8s Secret + passwordKey: password # name of the key that stores the root password + ``` + + If you are storing the database passwords in clear text (NOT recommended): + + ```yaml + # Old values + database: + user: fusionauth + password: password + root: + user: postgres + password: password + + # New values + database: + dbUser: + username: fusionauth + password: password + rootUser: + username: postgres + password: password + ``` + + 📝 Whether you use the new shape or not, if you are not using `existingSecret`, + the chart will now create separate Secrets for the database user and the root + user, instead of putting both passwords into a single secret. + +- `initContainers.waitForEs` renamed to `initContainers.waitForSearch` + +- Values for `search` credentials have changed. + - A `basicAuth` key was added to prepare for support of other credential types in the future. + - `search.basicAuth` now supports `existingSecret`. + + ```yaml + # Old values + search: + user: username # name of the search user + password: password # password for the search user + + # New values + search: + basicAuth: + enabled: true + username: username + password: password + + # New values with existingSecret + search: + basicAuth: + existingSecret: + enabled: true + name: fusionauth-search-creds + userKey: username + passwordKey: password + ``` + +### 1.57.1 + +- **The chart version now matches the FusionAuth app version.** + + ⚠️ You can (and probably should) override the `image.tag` field in `values.yaml` to pin the desired version of the FusionAuth application. This ensures that upgrading the helm chart doesn't unexpectedly upgrade the FusionAuth version. + +### 1.0.0 + +- **The FusionAuth app version will now default to the latest available at the time of the chart's release.** Release notes will indicate the FusionAuth version included in the chart. + + ⚠️ You can (and probably should) override the `image.tag` field in `values.yaml` to pin the desired version of the FusionAuth application. This ensures that upgrading the helm chart doesn't unexpectedly upgrade the FusionAuth version. + +### 0.8.0 + +- **The `environment` value is now an array instead of an object.** Make sure to reformat your values when you update. + +### 0.4.0 + +- **The external postgresql and elasticsearch charts were dropped.** You will need to maintain those dependencies on your own. + +## Installing the Chart + +You can read the official instructions, including install steps for AWS, GCP, and Azure, in the [FusionAuth Kubernetes installation guide](https://fusionauth.io/docs/get-started/download-and-install/kubernetes/fusionauth-deployment). + +### Prerequisites + +- PostgreSQL or MySQL database +- ElasticSearch or OpenSearch instance (optional) + +⚠️ Though an ElasticSearch or OpenSearch instance is optional, it is strongly recommended for most use cases. + +### Installation + +To install the chart with the release name `fusionauth`: + +```shell +helm repo add fusionauth https://fusionauth.github.io/charts +helm install fusionauth fusionauth/fusionauth \ + --set database.host=[database host] \ + --set database.dbUser.username=[database username] \ + --set database.dbUser.password=[database password] \ + --set search.host=[elasticsearch host] +``` + +## Setting Up a Test Deployment + +This will install FusionAuth and its prerequisites in a single kubernetes namespace, with a configuration suitable for evaluation and testing. **This configuration is not suitable for production.** + +Create and switch to the test namespace. + +```shell +kubectl create namespace fusionauth-test +kubectl config set-context --current --namespace=fusionauth-test +``` + +### Install PostgreSQL + +```shell +helm install postgres oci://registry-1.docker.io/bitnamicharts/postgresql +``` + +### Install Opensearch + +Opensearch is optional, but highly recommended. See the note below. + +```shell +helm repo add opensearch https://opensearch-project.github.io/helm-charts/ +helm install opensearch opensearch/opensearch \ +--set singleNode=true \ +--set-json 'extraEnvs=[{"name":"DISABLE_SECURITY_PLUGIN","value":"true"}]' +``` + +### Install FusionAuth + +Wait for the Postgres and Opensearch pods to be ready, then install FusionAuth. + +```shell +export FA_PSQL_PASS=$(kubectl get secret postgres-postgresql -o jsonpath="{.data.postgres-password}" | base64 -d) +helm repo add fusionauth https://fusionauth.github.io/charts +helm install fusionauth fusionauth/fusionauth \ +--set database.host=postgres-postgresql \ +--set database.dbUser.username=fusionauth \ +--set database.dbUser.password=$FA_PSQL_PASS \ +--set search.host=opensearch-cluster-master +``` + +📝 For test deployments, you can remove `--set search.host` and add `--set search.engine=database` to configure FusionAuth to use the database for search instead of a dedicated search host. This is **not recommended** for real-world use, as search performance will be greatly reduced. + +### Connect to FusionAuth + +Create a port forward to connect to the FusionAuth app. + +```shell +kubectl port-forward svc/fusionauth 9011:9011 +``` + +You should now be able to connect to the FusionAuth application at http://localhost:9011 to start the initial setup. + +📝 You may wish to set up an ingress instead of using a port forward. See the table below for how to configure the FusionAuth chart values to add an ingress. + +## Chart Values + +{{ template "chart.valuesTable" . }} diff --git a/chart/examples/minikube/values.yaml b/chart/examples/minikube/values.yaml index 4ab4a47..15056a6 100644 --- a/chart/examples/minikube/values.yaml +++ b/chart/examples/minikube/values.yaml @@ -7,8 +7,8 @@ replicaCount: 1 image: # image.repository -- The name of the docker repository for fusionauth-app repository: fusionauth/fusionauth-app - # image.repository -- The docker tag to pull for fusionauth-app - tag: ${APP_VERSION} + # image.tag -- The docker tag to pull for fusionauth-app + tag: 0.0.0-app-dev # image.pullPolicy -- Kubernetes image pullPolicy to use for fusionauth-app pullPolicy: IfNotPresent @@ -20,11 +20,10 @@ initContainers: image: # initContainers.image.repository -- Docker image to use for initContainers repository: busybox - # initContainers.image.repository -- Tag to use for initContainers docker image + # initContainers.image.tag -- Tag to use for initContainers docker image tag: latest # initContainers.resources -- Resource requests and limits to use for initContainers - resources: - {} + resources: {} # We usually recommend not to specify default resources and to leave this as a conscious # choice for the user. This also increases chances charts run on environments with little # resources, such as Minikube. If you do want to specify resources, uncomment the following @@ -48,15 +47,13 @@ service: port: 9011 # service.annotations -- Extra annotations to add to service object annotations: {} - # service.spec -- Any extra fields to add to the service object spec - spec: {} database: # database.protocol -- Should either be postgresql or mysql. Protocol for jdbc connection to database protocol: postgresql # database.host -- Hostname or ip of the database instance host: pg-minikube-postgresql.default.svc.cluster.local - # database.host -- Port of the database instance + # database.port -- Port of the database instance port: 5432 # database.tls -- Configures whether or not to use tls when connecting to the database tls: false @@ -65,33 +62,62 @@ database: # database.name -- Name of the fusionauth database name: fusionauth - # To use an existing secret, set `existingSecret` to the name of the secret. We expect at most two keys: `password` is required. `rootpassword` is only required if `database.root.user` is set. - # database.existingSecret -- The name of an existing secret that contains the database passwords - existingSecret: "" - # database.user -- Database username for fusionauth to use in normal operation - user: "fusionauth" - # database.password -- Database password for fusionauth to use in normal operation - not required if database.existingSecret is configured - password: "minikube-password" - # These credentials are used for bootstrapping the database - root: - # database.root.user -- Database username for fusionauth to use during initial bootstrap - not required if you have manually bootstrapped your database + # database.dbUser -- Database credentials for fusionauth to use in normal operation + dbUser: + # database.dbUser.username -- Database username for fusionauth to use in normal operation + username: "fusionauth" + # database.dbUser.password -- Database password for fusionauth to use in normal operation - not required if database.dbUser.existingSecret.enabled is true + password: "minikube-password" + # database.dbUser.existingSecret -- Configures an existing secret that contains the normal database user password. + existingSecret: + # database.dbUser.existingSecret.enabled -- Use an existing secret for the normal database user password. + enabled: false + # database.dbUser.existingSecret.name -- The name of an existing secret that contains the normal database user password. + name: "" + # database.dbUser.existingSecret.passwordKey -- The key in database.dbUser.existingSecret.name that contains the database password. + passwordKey: password + # database.rootUser -- Database credentials for fusionauth to use during initial bootstrap + rootUser: + # database.rootUser.username -- Database username for fusionauth to use during initial bootstrap - not required if you have manually bootstrapped your database username: "postgres" - # database.root.password -- Database password for fusionauth to use during initial bootstrap - not required if database.existingSecret is configured - password: "" + # database.rootUser.password -- Database password for fusionauth to use during initial bootstrap - not required if database.rootUser.existingSecret.enabled is true + password: "minikube-password" + # database.rootUser.existingSecret -- Configures an existing secret that contains the root database user password. + existingSecret: + # database.rootUser.existingSecret.enabled -- Use an existing secret for the root database user password. + enabled: false + # database.rootUser.existingSecret.name -- The name of an existing secret that contains the root database user password. + name: "" + # database.rootUser.existingSecret.passwordKey -- The key in database.rootUser.existingSecret.name that contains the root database password. + passwordKey: password search: # search.engine -- Defines backend for fusionauth search capabilities. Valid values for engine are 'elasticsearch' or 'database'. engine: elasticsearch - # search.engine -- Protocol to use when connecting to elasticsearch. Ignored when search.engine is NOT elasticsearch + # search.protocol -- Protocol to use when connecting to elasticsearch. Ignored when search.engine is NOT elasticsearch protocol: http # search.host -- Hostname or ip to use when connecting to elasticsearch. Ignored when search.engine is NOT elasticsearch host: elasticsearch-master.default.svc.cluster.local # search.port -- Port to use when connecting to elasticsearch. Ignored when search.engine is NOT elasticsearch port: 9200 - # search.user -- Username to use with basic auth when connecting to elasticsearch. Ignored when search.engine is NOT elasticsearch - # user: "" - # search.password -- Password to use with basic auth when connecting to elasticsearch. Ignored when search.engine is NOT elasticsearch - # password: "" + # search.basicAuth -- Configures elasticsearch basic auth credentials. Ignored when search.engine is NOT elasticsearch. + basicAuth: + # search.basicAuth.enabled -- Enables elasticsearch basic auth using inline username/password. Not required when search.basicAuth.existingSecret.enabled is true. + enabled: false + # search.basicAuth.username -- Username to use with elasticsearch basic auth. Ignored when search.basicAuth.existingSecret.enabled is true. + username: "" + # search.basicAuth.password -- Password to use with elasticsearch basic auth. Ignored when search.basicAuth.existingSecret.enabled is true. + password: "" + # search.basicAuth.existingSecret -- Configures an existing secret that contains elasticsearch basic auth credentials. + existingSecret: + # search.basicAuth.existingSecret.enabled -- Use an existing secret for elasticsearch basic auth credentials. + enabled: false + # search.basicAuth.existingSecret.name -- The name of an existing secret that contains elasticsearch basic auth credentials. + name: "" + # search.basicAuth.existingSecret.userKey -- The key in search.basicAuth.existingSecret.name that contains the elasticsearch username. + userKey: username + # search.basicAuth.existingSecret.passwordKey -- The key in search.basicAuth.existingSecret.name that contains the elasticsearch password. + passwordKey: password app: # app.memory -- Configures the amount of memory Java can use @@ -102,8 +128,7 @@ app: silentMode: true # environment - Configure additional environment variables. Should only be used for things that are not explicitly set elsewhere in the chart. -environment: - [] +environment: [] # - name: POD_IP # valueFrom: # fieldRef: @@ -117,8 +142,7 @@ environment: kickstart: enabled: false - data: - {} + data: {} # kickstart.json: | # { # "variables": { @@ -156,8 +180,7 @@ kickstart: # setup-password.txt: | # Hallo -lifecycle: - {} +lifecycle: {} # # lifecycle.postStart -- postStart lifecycle command for fusionauth container # postStart: # exec: @@ -172,14 +195,11 @@ podDisruptionBudget: enabled: false ingress: - # ingress.enabled -- Enables ingress creation for fusionauth. enabled: true # ingress.annotations -- Configure annotations to add to the ingress object annotations: - { - kubernetes.io/ingress.class: nginx - } + { kubernetes.io/ingress.class: nginx } # kubernetes.io/tls-acme: "true" paths: - path: / @@ -192,7 +212,8 @@ ingress: # ingress.extraPaths -- Define complete path objects, will be inserted before regular paths. Can be useful for things like ALB Ingress Controller actions extraPaths: [] # ingress.hosts -- List of hostnames to configure the ingress with - hosts: ["localhost"] + hosts: + ["localhost"] # - chart-example.local # ingress.tls -- List of secrets used to configure TLS for the ingress. tls: [] @@ -201,8 +222,7 @@ ingress: # - chart-example.local # resources -- Define resource requests and limits for fusionauth-app. -resources: - {} +resources: {} # We usually recommend not to specify default resources and to leave this as a conscious # choice for the user. This also increases chances charts run on environments with little # resources, such as Minikube. If you do want to specify resources, uncomment the following @@ -224,7 +244,7 @@ nodeSelector: {} # tolerations -- Define tolerations for kubernetes to use when scheduling fusionauth pods. tolerations: [] -# affinty -- Define affinity for kubernetes to use when scheduling fusionauth pods. +# affinity -- Define affinity for kubernetes to use when scheduling fusionauth pods. affinity: {} # dnsConfig -- Define dnsConfig for fusionauth pods. @@ -262,14 +282,12 @@ startupProbe: timeoutSeconds: 5 # extraVolumes -- Define extra Volumes. Allow to add existing claimName -extraVolumes: - [] +extraVolumes: [] # - name: custom-css-data # persistentVolumeClaim: # claimName: custom-css-data -# extraVolumes -- Associate mountPath for each extraVolumes -extraVolumeMounts: - [] +# extraVolumeMounts -- Associate mountPath for each extraVolumes +extraVolumeMounts: [] # - name: custom-css-data # mountPath: /usr/local/fusionauth/fusionauth-app/web/custom diff --git a/chart/templates/NOTES.txt b/chart/templates/NOTES.txt index 5131590..5fb2266 100644 --- a/chart/templates/NOTES.txt +++ b/chart/templates/NOTES.txt @@ -2,7 +2,7 @@ {{- if .Values.ingress.enabled }} {{- range $host := .Values.ingress.hosts }} {{- range $.Values.ingress.paths }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host }}{{ .path }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host }}{{ .path | default "/" }} {{- end }} {{- end }} {{- else if contains "NodePort" .Values.service.type }} diff --git a/chart/templates/_database.tpl b/chart/templates/_database.tpl new file mode 100644 index 0000000..ff74570 --- /dev/null +++ b/chart/templates/_database.tpl @@ -0,0 +1,278 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Configure TLS if enabled +*/}} +{{- define "fusionauth.databaseTLS" -}} +{{- if .Values.database.tls -}} +?sslmode={{ .Values.database.tlsMode }} +{{- end -}} +{{- end -}} + +{{/* +Build DATABASE_URL from chart database values. +*/}} +{{- define "fusionauth.database.url" -}} +{{- if .Values.database.url -}} +{{- .Values.database.url -}} +{{- else -}} +jdbc:{{ .Values.database.protocol }}://{{- required "database.host is required" .Values.database.host -}}:{{ .Values.database.port }}/{{ .Values.database.name }}{{ include "fusionauth.databaseTLS" . }} +{{- end -}} +{{- end -}} + +{{/* +Resolve FusionAuth database username. +- Current: database.dbUser.username +- Legacy: database.user, which takes precedence when set for compatibility +*/}} +{{- define "fusionauth.database.dbUser.username" -}} +{{- $database := .Values.database | default dict -}} +{{- $dbUser := $database.dbUser | default dict -}} +{{- if $database.user -}} +{{- $database.user -}} +{{- else -}} +{{- $dbUser.username | default "" -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve FusionAuth database password. +- Current: database.dbUser.password +- Legacy: database.password +*/}} +{{- define "fusionauth.database.dbUser.password" -}} +{{- $database := .Values.database | default dict -}} +{{- $dbUser := $database.dbUser | default dict -}} +{{- if $dbUser.password -}} +{{- $dbUser.password -}} +{{- else -}} +{{- $database.password | default "" -}} +{{- end -}} +{{- end -}} + +{{/* +Validate FusionAuth database credential combinations. +*/}} +{{- define "fusionauth.database.dbUser.validate" -}} +{{- $database := .Values.database | default dict -}} +{{- $dbUser := $database.dbUser | default dict -}} +{{- if and $database.password (not $database.user) -}} +{{- fail "database.user is required when database.password is set" -}} +{{- end -}} +{{- if and $dbUser.password (not $dbUser.username) -}} +{{- fail "database.dbUser.username is required when database.dbUser.password is set" -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve whether FusionAuth database credentials should come from an existing +secret. +- Current: database.dbUser.existingSecret.enabled +- Legacy: database.existingSecret string +*/}} +{{- define "fusionauth.database.dbUser.existingSecret.enabled" -}} +{{- $database := .Values.database | default dict -}} +{{- $dbUser := $database.dbUser | default dict -}} +{{- $dbUserExistingSecret := $dbUser.existingSecret | default dict -}} +{{- $legacyExistingSecret := $database.existingSecret -}} +{{- if $dbUserExistingSecret.enabled -}} +true +{{- else if kindIs "string" $legacyExistingSecret -}} +{{- if $legacyExistingSecret -}}true{{- else -}}false{{- end -}} +{{- else -}} +false +{{- end -}} +{{- end -}} + +{{/* +Resolve the FusionAuth database Secret name. +- Current: database.dbUser.existingSecret.name +- Legacy: database.existingSecret +*/}} +{{- define "fusionauth.database.dbUser.secretName" -}} +{{- $database := .Values.database | default dict -}} +{{- $dbUser := $database.dbUser | default dict -}} +{{- $dbUserExistingSecret := $dbUser.existingSecret | default dict -}} +{{- $legacyExistingSecret := $database.existingSecret -}} +{{- if eq (include "fusionauth.database.dbUser.existingSecret.enabled" .) "true" -}} +{{- if $dbUserExistingSecret.enabled -}} +{{- required "database.dbUser.existingSecret.name is required when database.dbUser.existingSecret.enabled is true" $dbUserExistingSecret.name -}} +{{- else if kindIs "string" $legacyExistingSecret -}} +{{- required "database.existingSecret must not be empty when used as a secret name" $legacyExistingSecret -}} +{{- else -}} +{{- required "database.dbUser.existingSecret.name is required when database.dbUser.existingSecret.enabled is true" $dbUserExistingSecret.name -}} +{{- end -}} +{{- else -}} +{{ .Release.Name }}-db-credentials +{{- end -}} +{{- end -}} + +{{/* +Resolve the FusionAuth database password key. +- Current: database.dbUser.existingSecret.passwordKey +- Legacy: n/a +*/}} +{{- define "fusionauth.database.dbUser.passwordKey" -}} +{{- $database := .Values.database | default dict -}} +{{- $dbUser := $database.dbUser | default dict -}} +{{- $dbUserExistingSecret := $dbUser.existingSecret | default dict -}} +{{- if and $dbUserExistingSecret.enabled $dbUserExistingSecret.passwordKey -}} +{{- $dbUserExistingSecret.passwordKey -}} +{{- else -}} +password +{{- end -}} +{{- end -}} + +{{/* +Resolve whether the chart should create the FusionAuth database credentials +Secret. +*/}} +{{- define "fusionauth.database.dbUser.generatedSecret.enabled" -}} +{{- $existingSecret := eq (include "fusionauth.database.dbUser.existingSecret.enabled" .) "true" -}} +{{- if not $existingSecret -}}true{{- else -}}false{{- end -}} +{{- end -}} + +{{/* +Resolve root database username. +- Current: database.rootUser.username +- Legacy: database.root.user, which takes precedence when set for compatibility +*/}} +{{- define "fusionauth.database.rootUser.username" -}} +{{- $database := .Values.database | default dict -}} +{{- $rootUser := $database.rootUser | default dict -}} +{{- $legacyRootUser := $database.root | default dict -}} +{{- if $legacyRootUser.user -}} +{{- $legacyRootUser.user -}} +{{- else -}} +{{- $rootUser.username | default "" -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve root database password. +- Current: database.rootUser.password +- Legacy: database.root.password +*/}} +{{- define "fusionauth.database.rootUser.password" -}} +{{- $database := .Values.database | default dict -}} +{{- $rootUser := $database.rootUser | default dict -}} +{{- $legacyRootUser := $database.root | default dict -}} +{{- if $rootUser.password -}} +{{- $rootUser.password -}} +{{- else -}} +{{- $legacyRootUser.password | default "" -}} +{{- end -}} +{{- end -}} + +{{/* +Validate root database credential combinations. +*/}} +{{- define "fusionauth.database.rootUser.validate" -}} +{{- $database := .Values.database | default dict -}} +{{- $rootUser := $database.rootUser | default dict -}} +{{- $legacyRootUser := $database.root | default dict -}} +{{- if and $legacyRootUser.password (not $legacyRootUser.user) -}} +{{- fail "database.root.user is required when database.root.password is set" -}} +{{- end -}} +{{- if and $rootUser.password (not $rootUser.username) -}} +{{- fail "database.rootUser.username is required when database.rootUser.password is set" -}} +{{- end -}} +{{- end -}} + +{{/* +Validate database credential combinations that span the dbUser and rootUser. +*/}} +{{- define "fusionauth.database.validate" -}} +{{- include "fusionauth.environment.validate" . }} +{{- include "fusionauth.database.dbUser.validate" . }} +{{- include "fusionauth.database.rootUser.validate" . }} +{{- if and .Values.database.url (eq (include "fusionauth.initContainers.waitForDb" .) "true") (not .Values.database.host) -}} +{{- fail "database.host is required when database.url is set and initContainers.waitForDb is true" -}} +{{- end -}} +{{- $dbUserExistingSecretEnabled := eq (include "fusionauth.database.dbUser.existingSecret.enabled" .) "true" -}} +{{- $rootUserExistingSecretEnabled := eq (include "fusionauth.database.rootUser.existingSecret.enabled" .) "true" -}} +{{- if and $dbUserExistingSecretEnabled $rootUserExistingSecretEnabled -}} +{{- $dbUserSecretName := include "fusionauth.database.dbUser.secretName" . -}} +{{- $rootUserSecretName := include "fusionauth.database.rootUser.secretName" . -}} +{{- $dbUserPasswordKey := include "fusionauth.database.dbUser.passwordKey" . -}} +{{- $rootUserPasswordKey := include "fusionauth.database.rootUser.passwordKey" . -}} +{{- if and (eq $dbUserSecretName $rootUserSecretName) (eq $dbUserPasswordKey $rootUserPasswordKey) -}} +{{- fail "passwordKey values must be different when database.dbUser.existingSecret.name and database.rootUser.existingSecret.name reference the same Secret" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve whether root database credentials should come from an existing secret. +- Current: database.rootUser.existingSecret.enabled +- Legacy: database.existingSecret string +*/}} +{{- define "fusionauth.database.rootUser.existingSecret.enabled" -}} +{{- $database := .Values.database | default dict -}} +{{- $rootUser := $database.rootUser | default dict -}} +{{- $rootUserExistingSecret := $rootUser.existingSecret | default dict -}} +{{- $legacyExistingSecret := $database.existingSecret -}} +{{- if $rootUserExistingSecret.enabled -}} +true +{{- else if kindIs "string" $legacyExistingSecret -}} +{{- if $legacyExistingSecret -}}true{{- else -}}false{{- end -}} +{{- else -}} +false +{{- end -}} +{{- end -}} + +{{/* +Resolve the root database Secret name. +- Current: database.rootUser.existingSecret.name +- Legacy: database.existingSecret +*/}} +{{- define "fusionauth.database.rootUser.secretName" -}} +{{- $database := .Values.database | default dict -}} +{{- $rootUser := $database.rootUser | default dict -}} +{{- $rootUserExistingSecret := $rootUser.existingSecret | default dict -}} +{{- $legacyExistingSecret := $database.existingSecret -}} +{{- if eq (include "fusionauth.database.rootUser.existingSecret.enabled" .) "true" -}} +{{- if $rootUserExistingSecret.enabled -}} +{{- required "database.rootUser.existingSecret.name is required when database.rootUser.existingSecret.enabled is true" $rootUserExistingSecret.name -}} +{{- else if kindIs "string" $legacyExistingSecret -}} +{{- required "database.existingSecret must not be empty when used as a secret name" $legacyExistingSecret -}} +{{- else -}} +{{- required "database.rootUser.existingSecret.name is required when database.rootUser.existingSecret.enabled is true" $rootUserExistingSecret.name -}} +{{- end -}} +{{- else -}} +{{ .Release.Name }}-db-root-credentials +{{- end -}} +{{- end -}} + +{{/* +Resolve the root database password key. +- Current: database.rootUser.existingSecret.passwordKey +- Legacy: database.existingSecret string uses rootpassword +*/}} +{{- define "fusionauth.database.rootUser.passwordKey" -}} +{{- $database := .Values.database | default dict -}} +{{- $rootUser := $database.rootUser | default dict -}} +{{- $rootUserExistingSecret := $rootUser.existingSecret | default dict -}} +{{- $legacyExistingSecret := $database.existingSecret -}} +{{- if and $rootUserExistingSecret.enabled $rootUserExistingSecret.passwordKey -}} +{{- $rootUserExistingSecret.passwordKey -}} +{{- else if and (not $rootUserExistingSecret.enabled) $legacyExistingSecret (kindIs "string" $legacyExistingSecret) -}} +rootpassword +{{- else -}} +password +{{- end -}} +{{- end -}} + +{{/* +Resolve whether root database credentials are configured. +*/}} +{{- define "fusionauth.database.rootUser.configured" -}} +{{- if include "fusionauth.database.rootUser.username" . -}}true{{- else -}}false{{- end -}} +{{- end -}} + +{{/* +Resolve whether the chart should create the root database credentials Secret. +*/}} +{{- define "fusionauth.database.rootUser.generatedSecret.enabled" -}} +{{- $existingSecret := eq (include "fusionauth.database.rootUser.existingSecret.enabled" .) "true" -}} +{{- if and (eq (include "fusionauth.database.rootUser.configured" .) "true") (not $existingSecret) -}}true{{- else -}}false{{- end -}} +{{- end -}} diff --git a/chart/templates/_deployment.tpl b/chart/templates/_deployment.tpl new file mode 100644 index 0000000..28c49fb --- /dev/null +++ b/chart/templates/_deployment.tpl @@ -0,0 +1,118 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Resolve the reserved kickstart config volume name. +*/}} +{{- define "fusionauth.kickstart.volumeName" -}} +{{- printf "%s-config-volume" (include "fusionauth.fullname" .) -}} +{{- end -}} + +{{/* +Resolve whether the database wait init container should be rendered. +*/}} +{{- define "fusionauth.deployment.waitForDb.enabled" -}} +{{- if eq (include "fusionauth.initContainers.waitForDb" .) "true" -}}true{{- else -}}false{{- end -}} +{{- end -}} + +{{/* +Resolve whether the search wait init container should be rendered. +*/}} +{{- define "fusionauth.deployment.waitForSearch.enabled" -}} +{{- if and (eq (include "fusionauth.search.chartEnabled" .) "true") (eq (include "fusionauth.initContainers.waitForSearch" .) "true") .Values.search.host -}}true{{- else -}}false{{- end -}} +{{- end -}} + +{{/* +Validate deployment-only conflicts before rendering the Deployment manifest. +*/}} +{{- define "fusionauth.deployment.validate" -}} +{{- include "fusionauth.database.validate" . }} +{{- if hasKey .Values.podLabels "app.kubernetes.io/name" }} +{{- fail "podLabels cannot override reserved selector label app.kubernetes.io/name" }} +{{- end }} +{{- if hasKey .Values.podLabels "app.kubernetes.io/instance" }} +{{- fail "podLabels cannot override reserved selector label app.kubernetes.io/instance" }} +{{- end }} +{{- $kickstartVolumeName := include "fusionauth.kickstart.volumeName" . }} +{{- if .Values.kickstart.enabled }} +{{- range .Values.extraVolumes }} +{{- if eq .name $kickstartVolumeName }} +{{- fail (printf "extraVolumes cannot use reserved kickstart volume name %s" $kickstartVolumeName) }} +{{- end }} +{{- end }} +{{- range .Values.extraVolumeMounts }} +{{- if eq .name $kickstartVolumeName }} +{{- fail (printf "extraVolumeMounts cannot use reserved kickstart volume name %s" $kickstartVolumeName) }} +{{- end }} +{{- if eq .mountPath "/kickstart" }} +{{- fail "extraVolumeMounts cannot use reserved kickstart mountPath /kickstart when kickstart.enabled is true" }} +{{- end }} +{{- end }} +{{- end }} +{{- range .Values.extraInitContainers }} +{{- if has .name (list "wait-for-db" "wait-for-search") }} +{{- fail (printf "extraInitContainers cannot use reserved init container name %s" .name) }} +{{- end }} +{{- end }} +{{- range .Values.extraContainers }} +{{- if eq .name $.Chart.Name }} +{{- fail (printf "extraContainers cannot use reserved container name %s" $.Chart.Name) }} +{{- end }} +{{- end }} +{{- end -}} + +{{/* +Render FusionAuth container environment variables. +*/}} +{{- define "fusionauth.deployment.env" -}} +{{- $databaseRootUserConfigured := eq (include "fusionauth.database.rootUser.configured" .) "true" -}} +{{- $searchExistingSecretEnabled := .Values.search.basicAuth.existingSecret.enabled -}} +{{- $chartSearchEnabled := eq (include "fusionauth.search.chartEnabled" .) "true" -}} +{{- if .Values.environment }} +{{ toYaml .Values.environment }} +{{- end }} +- name: DATABASE_USERNAME + value: {{ required "database.dbUser.username is required; legacy database.user is also accepted" (include "fusionauth.database.dbUser.username" .) | quote }} +- name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "fusionauth.database.dbUser.secretName" . }} + key: {{ include "fusionauth.database.dbUser.passwordKey" . | quote }} +{{- if $databaseRootUserConfigured }} +- name: DATABASE_ROOT_USERNAME + value: {{ include "fusionauth.database.rootUser.username" . | quote }} +- name: DATABASE_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "fusionauth.database.rootUser.secretName" . }} + key: {{ include "fusionauth.database.rootUser.passwordKey" . | quote }} +{{- end }} +- name: DATABASE_URL + value: {{ include "fusionauth.database.url" . | quote }} +- name: SEARCH_TYPE + value: {{ .Values.search.engine | quote }} +{{- if $chartSearchEnabled }} +{{- if $searchExistingSecretEnabled }} +- name: SEARCH_USERNAME + valueFrom: + secretKeyRef: + name: {{ required "search.basicAuth.existingSecret.name is required when search basic auth uses an existing secret" .Values.search.basicAuth.existingSecret.name | quote }} + key: {{ .Values.search.basicAuth.existingSecret.userKey | default "username" | quote }} +- name: SEARCH_PASSWORD + valueFrom: + secretKeyRef: + name: {{ required "search.basicAuth.existingSecret.name is required when search basic auth uses an existing secret" .Values.search.basicAuth.existingSecret.name | quote }} + key: {{ .Values.search.basicAuth.existingSecret.passwordKey | default "password" | quote }} +{{- end }} +- name: SEARCH_SERVERS + value: "{{ .Values.search.protocol }}://{{ include "fusionauth.searchLogin" . }}{{- required "search.host is required when search.engine is elasticsearch" .Values.search.host -}}:{{ .Values.search.port }}" +{{- end }} +- name: FUSIONAUTH_APP_MEMORY + value: {{ .Values.app.memory | quote }} +- name: FUSIONAUTH_APP_RUNTIME_MODE + value: {{ .Values.app.runtimeMode | quote }} +- name: FUSIONAUTH_APP_SILENT_MODE + value: {{ .Values.app.silentMode | quote }} +{{- if .Values.kickstart.enabled }} +- name: FUSIONAUTH_APP_KICKSTART_FILE + value: {{ .Values.kickstart.file | quote }} +{{- end }} +{{- end -}} diff --git a/chart/templates/_helpers.tpl b/chart/templates/_helpers.tpl index a5befc9..8da67b8 100644 --- a/chart/templates/_helpers.tpl +++ b/chart/templates/_helpers.tpl @@ -25,84 +25,28 @@ If release name contains chart name it will be used as a full name. {{- end -}} {{/* -Set apiVersion for HPA -*/}} -{{- define "fusionauth.HpaApiVersion" -}} -{{- if .Capabilities.APIVersions.Has "autoscaling/v2" -}} -autoscaling/v2 -{{- else -}} -autoscaling/v2beta2 -{{- end -}} -{{- end -}} - - -{{/* -Set apiVersion for ingress -*/}} -{{- define "fusionauth.ingressApiVersion" -}} -{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" -}} -networking.k8s.io/v1 -{{- else if .Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" -}} -networking.k8s.io/v1beta1 -{{- else -}} -extensions/v1beta1 +Fail when .Values.environment attempts to override chart-managed FusionAuth +environment variables. Use the corresponding chart values instead. +*/}} +{{- define "fusionauth.environment.validate" -}} +{{- $reserved := list + "DATABASE_USERNAME" + "DATABASE_PASSWORD" + "DATABASE_ROOT_USERNAME" + "DATABASE_ROOT_PASSWORD" + "DATABASE_URL" + "SEARCH_TYPE" + "SEARCH_USERNAME" + "SEARCH_PASSWORD" + "SEARCH_SERVERS" + "FUSIONAUTH_APP_MEMORY" + "FUSIONAUTH_APP_RUNTIME_MODE" + "FUSIONAUTH_APP_SILENT_MODE" + "FUSIONAUTH_APP_KICKSTART_FILE" +-}} +{{- range .Values.environment -}} +{{- if has .name $reserved -}} +{{- fail (printf "environment cannot override chart-managed variable %s; use the corresponding chart value instead" .name) -}} {{- end -}} {{- end -}} - -{{/* -Set apiVersion for PodDisruptionBudget -*/}} -{{- define "fusionauth.PodDisruptionBudget" -}} -{{- if .Capabilities.APIVersions.Has "policy/v1" -}} -policy/v1 -{{- else -}} -policy/v1beta1 -{{- end -}} -{{- end -}} - - -{{/* -Configure TLS if enabled -*/}} -{{- define "fusionauth.databaseTLS" -}} -{{- if .Values.database.tls -}} -?sslmode={{ .Values.database.tlsMode }} {{- end -}} -{{- end -}} - -{{- define "fusionauth.searchLogin" -}} -{{- if .Values.search.user -}} -{{- printf "%s:%s@" .Values.search.user .Values.search.password -}} -{{- else -}} -{{- printf "" -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "fusionauth.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Set name of secret to use for credentials -*/}} -{{- define "fusionauth.database.secretName" -}} -{{- if .Values.database.existingSecret -}} -{{- .Values.database.existingSecret -}} -{{- else -}} -{{ .Release.Name }}-credentials -{{- end -}} -{{- end -}} - -{{/* -Create the name of the service account to use -*/}} -{{- define "fusionauth.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "fusionauth.fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} diff --git a/chart/templates/_init_containers.tpl b/chart/templates/_init_containers.tpl new file mode 100644 index 0000000..4a475aa --- /dev/null +++ b/chart/templates/_init_containers.tpl @@ -0,0 +1,26 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Resolve the database wait init-container flag. +*/}} +{{- define "fusionauth.initContainers.waitForDb" -}} +{{- if hasKey .Values.initContainers "waitForDb" -}} +{{- .Values.initContainers.waitForDb -}} +{{- else -}} +true +{{- end -}} +{{- end -}} + +{{/* +Resolve the search wait init-container flag. +Current value: initContainers.waitForSearch. +Backward compatibility: deprecated initContainers.waitForEs is still accepted. +*/}} +{{- define "fusionauth.initContainers.waitForSearch" -}} +{{- if hasKey .Values.initContainers "waitForEs" -}} +{{- .Values.initContainers.waitForEs -}} +{{- else if hasKey .Values.initContainers "waitForSearch" -}} +{{- .Values.initContainers.waitForSearch -}} +{{- else -}} +true +{{- end -}} +{{- end -}} diff --git a/chart/templates/_labels.tpl b/chart/templates/_labels.tpl new file mode 100644 index 0000000..149ee26 --- /dev/null +++ b/chart/templates/_labels.tpl @@ -0,0 +1,37 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "fusionauth.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Common labels applied to FusionAuth resources. +*/}} +{{- define "fusionauth.labels" -}} +app.kubernetes.io/name: {{ include "fusionauth.name" . }} +helm.sh/chart: {{ include "fusionauth.chart" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Selector labels used by Services, workload selectors, and pods. +Keep these stable because selector labels are immutable on several resources. +*/}} +{{- define "fusionauth.selectorLabels" -}} +app.kubernetes.io/name: {{ include "fusionauth.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "fusionauth.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "fusionauth.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/chart/templates/_search.tpl b/chart/templates/_search.tpl new file mode 100644 index 0000000..abef65a --- /dev/null +++ b/chart/templates/_search.tpl @@ -0,0 +1,54 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Resolve whether search basic auth is enabled. +Current value: search.basicAuth.enabled. +Backward compatibility: deprecated search.user/search.password still imply that +basic auth is enabled. +*/}} +{{- define "fusionauth.search.basicAuth.enabled" -}} +{{- if .Values.search.basicAuth.enabled -}} +true +{{- else if or .Values.search.user .Values.search.password .Values.search.basicAuth.existingSecret.enabled -}} +true +{{- else -}} +false +{{- end -}} +{{- end -}} + +{{/* +Build the search login prefix for SEARCH_SERVERS. +Backward compatibility: deprecated search.user/search.password still render the +same URL prefix. +*/}} +{{- define "fusionauth.searchLogin" -}} +{{- if .Values.search.basicAuth.existingSecret.enabled -}} +$(SEARCH_USERNAME):$(SEARCH_PASSWORD)@ +{{- else if eq (include "fusionauth.search.basicAuth.enabled" .) "true" -}} +{{- $username := "" -}} +{{- $password := "" -}} +{{- if .Values.search.basicAuth.enabled -}} +{{- $username = .Values.search.basicAuth.username -}} +{{- $password = .Values.search.basicAuth.password -}} +{{- if not $username -}} +{{- fail "search.basicAuth.username is required when search.basicAuth.enabled is true and search.basicAuth.existingSecret.enabled is false" -}} +{{- end -}} +{{- if not $password -}} +{{- fail "search.basicAuth.password is required when search.basicAuth.enabled is true and search.basicAuth.existingSecret.enabled is false" -}} +{{- end -}} +{{- else -}} +{{- $username = .Values.search.user -}} +{{- $password = .Values.search.password -}} +{{- end -}} +{{- printf "%s:%s@" $username $password -}} +{{- else -}} +{{- printf "" -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve whether chart-managed Elasticsearch/OpenSearch env and wait behavior +should be rendered. +*/}} +{{- define "fusionauth.search.chartEnabled" -}} +{{- if eq .Values.search.engine "elasticsearch" -}}true{{- else -}}false{{- end -}} +{{- end -}} diff --git a/chart/templates/configmap.yaml b/chart/templates/configmap.yaml index e0fdba5..8e5c2b3 100644 --- a/chart/templates/configmap.yaml +++ b/chart/templates/configmap.yaml @@ -1,18 +1,16 @@ {{- if .Values.kickstart.enabled }} +{{- if not .Values.kickstart.data }} +{{- fail "kickstart.data must contain at least one file when kickstart.enabled is true" }} +{{- end }} apiVersion: v1 kind: ConfigMap metadata: name: {{ template "fusionauth.fullname" . }}-kickstart-config labels: - heritage: {{ .Release.Name }} - release: {{ .Release.Name }} - chart: {{ .Chart.Name }}-{{ .Chart.Version }} - app: {{ template "fusionauth.name" . }} + {{- include "fusionauth.labels" . | nindent 4 }} data: - {{- if .Values.kickstart.data }} {{- range $key, $value := .Values.kickstart.data }} {{ $key }}: | {{ $value | indent 4 }} {{- end -}} {{- end -}} -{{- end -}} diff --git a/chart/templates/deployment.yaml b/chart/templates/deployment.yaml index 0c8291a..097cbc5 100644 --- a/chart/templates/deployment.yaml +++ b/chart/templates/deployment.yaml @@ -1,12 +1,13 @@ +{{- include "fusionauth.deployment.validate" . }} +{{- $waitForDb := eq (include "fusionauth.deployment.waitForDb.enabled" .) "true" }} +{{- $waitForSearch := eq (include "fusionauth.deployment.waitForSearch.enabled" .) "true" }} +{{- $kickstartVolumeName := include "fusionauth.kickstart.volumeName" . }} apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "fusionauth.fullname" . }} labels: - app.kubernetes.io/name: {{ include "fusionauth.name" . }} - helm.sh/chart: {{ include "fusionauth.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- include "fusionauth.labels" . | nindent 4 }} {{- with .Values.annotations }} annotations: {{- toYaml . | nindent 4 }} @@ -17,15 +18,13 @@ spec: {{- end }} selector: matchLabels: - app.kubernetes.io/name: {{ include "fusionauth.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} + {{- include "fusionauth.selectorLabels" . | nindent 6 }} template: metadata: labels: - app.kubernetes.io/name: {{ include "fusionauth.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} + {{- include "fusionauth.selectorLabels" . | nindent 8 }} {{- with .Values.podLabels }} - {{- toYaml . | nindent 8 }} + {{- toYaml . | nindent 8 }} {{- end }} {{- with .Values.podAnnotations }} annotations: @@ -37,10 +36,10 @@ spec: imagePullSecrets: {{- toYaml .Values.imagePullSecrets | nindent 8 }} {{- end }} - {{- if or (.Values.extraInitContainers) (or (.Values.initContainers.waitForDb) (and (eq .Values.search.engine "elasticsearch") (.Values.initContainers.waitForEs))) }} + {{- if or (.Values.extraInitContainers) (or $waitForDb $waitForSearch) }} initContainers: {{- end }} - {{- if .Values.initContainers.waitForDb }} + {{- if $waitForDb }} - name: wait-for-db image: "{{ .Values.initContainers.image.repository }}:{{ .Values.initContainers.image.tag }}" args: @@ -48,14 +47,14 @@ spec: - -c - > set -x; - while [[ "$(nc -zv '{{- .Values.database.host -}}' {{ .Values.database.port }} &> /dev/null; echo $?)" != 0 ]]; do + until nc -zv '{{- .Values.database.host -}}' {{ .Values.database.port }} >/dev/null 2>&1; do echo '.' sleep 15; done resources: {{- toYaml .Values.initContainers.resources | nindent 12 }} {{- end }} - {{- if and (eq .Values.search.engine "elasticsearch") (.Values.initContainers.waitForEs) }} + {{- if $waitForSearch }} - name: wait-for-search image: "{{ .Values.initContainers.image.repository }}:{{ .Values.initContainers.image.tag }}" args: @@ -63,7 +62,7 @@ spec: - -c - > set -x; - while [[ "$(nc -zv '{{- .Values.search.host -}}' {{ .Values.search.port }} &> /dev/null; echo $?)" != 0 ]]; do + until nc -zv '{{- .Values.search.host -}}' {{ .Values.search.port }} >/dev/null 2>&1; do echo '.' sleep 15; done @@ -92,45 +91,7 @@ spec: {{- toYaml . | nindent 12 }} {{- end }} env: - - name: DATABASE_USERNAME - value: {{ required "A valid username for the database is required!" .Values.database.user }} - - name: DATABASE_PASSWORD - valueFrom: - secretKeyRef: - name: {{ include "fusionauth.database.secretName" . }} - key: password - {{- if .Values.database.root.user }} - - name: DATABASE_ROOT_USERNAME - value: {{ .Values.database.root.user }} - - name: DATABASE_ROOT_PASSWORD - valueFrom: - secretKeyRef: - name: {{ include "fusionauth.database.secretName" . }} - key: rootpassword - {{- end }} - - name: DATABASE_URL - value: "jdbc:{{ .Values.database.protocol }}://{{- required "A valid database host is required!" .Values.database.host -}}:{{ .Values.database.port }}/{{ .Values.database.name }}{{ include "fusionauth.databaseTLS" . }}" - - name: SEARCH_TYPE - value: {{ .Values.search.engine }} - {{- if eq .Values.search.engine "elasticsearch" }} - - name: SEARCH_SERVERS - value: "{{ .Values.search.protocol }}://{{ include "fusionauth.searchLogin" . }}{{- required "A valid elasticsearch host is required!" .Values.search.host -}}:{{ .Values.search.port }}" - {{- end }} - - name: FUSIONAUTH_APP_MEMORY - value: {{ .Values.app.memory }} - - name: FUSIONAUTH_APP_RUNTIME_MODE - value: {{ .Values.app.runtimeMode }} - {{- if not (contains "FUSIONAUTH_APP_SILENT_MODE" (toString .Values.environment)) }} - - name: FUSIONAUTH_APP_SILENT_MODE - value: {{ .Values.app.silentMode | quote }} - {{- end }} - {{- if .Values.kickstart.enabled }} - - name: FUSIONAUTH_APP_KICKSTART_FILE - value: "/kickstart/kickstart.json" - {{- end }} - {{- if .Values.environment }} - {{- toYaml .Values.environment |nindent 12 }} - {{- end }} +{{ include "fusionauth.deployment.env" . | trim | indent 12 }} resources: {{- toYaml .Values.resources | nindent 12 }} {{- with .Values.securityContext }} @@ -143,7 +104,7 @@ spec: {{- end }} {{- if .Values.kickstart.enabled }} - - name: {{ template "fusionauth.fullname" . }}-config-volume + - name: {{ $kickstartVolumeName }} mountPath: /kickstart {{- end }} @@ -172,25 +133,25 @@ spec: {{- end }} {{- if .Values.kickstart.enabled }} - - name: {{ template "fusionauth.fullname" . }}-config-volume + - name: {{ $kickstartVolumeName }} configMap: name: {{ template "fusionauth.fullname" . }}-kickstart-config {{- end }} {{- with .Values.nodeSelector }} nodeSelector: - {{- toYaml . | nindent 8 }} + {{- toYaml . | nindent 8 }} {{- end }} - {{- with .Values.affinity }} + {{- with .Values.affinity }} affinity: {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} + {{- end }} + {{- with .Values.tolerations }} tolerations: {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.topologySpreadConstraints }} + {{- end }} + {{- with .Values.topologySpreadConstraints }} topologySpreadConstraints: {{- toYaml . | nindent 8 }} - {{- end }} + {{- end }} restartPolicy: Always diff --git a/chart/templates/hpa.yaml b/chart/templates/hpa.yaml index 66219b8..7b0d90f 100644 --- a/chart/templates/hpa.yaml +++ b/chart/templates/hpa.yaml @@ -1,13 +1,16 @@ {{- if .Values.autoscaling.enabled }} -apiVersion: {{ include "fusionauth.HpaApiVersion" . }} +{{- if not (or .Values.autoscaling.targetCPU .Values.autoscaling.targetMemory) }} +{{- fail "autoscaling.enabled requires at least one of autoscaling.targetCPU or autoscaling.targetMemory" }} +{{- end }} +{{- if gt (int .Values.autoscaling.minReplicas) (int .Values.autoscaling.maxReplicas) }} +{{- fail "autoscaling.minReplicas cannot be greater than autoscaling.maxReplicas" }} +{{- end }} +apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: {{ include "fusionauth.fullname" . }} labels: - app.kubernetes.io/name: {{ include "fusionauth.name" . }} - helm.sh/chart: {{ include "fusionauth.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- include "fusionauth.labels" . | nindent 4 }} spec: scaleTargetRef: apiVersion: apps/v1 diff --git a/chart/templates/ingress.yaml b/chart/templates/ingress.yaml index 46c844b..96fa71b 100644 --- a/chart/templates/ingress.yaml +++ b/chart/templates/ingress.yaml @@ -2,15 +2,18 @@ {{- $fullName := include "fusionauth.fullname" . -}} {{- $ingressPaths := .Values.ingress.paths -}} {{- $extraPaths := .Values.ingress.extraPaths -}} -apiVersion: {{ include "fusionauth.ingressApiVersion" . }} +{{- if not .Values.ingress.hosts -}} +{{- fail "ingress.enabled requires at least one ingress.hosts entry" -}} +{{- end -}} +{{- if not (or $ingressPaths $extraPaths) -}} +{{- fail "ingress.enabled requires at least one ingress.paths or ingress.extraPaths entry" -}} +{{- end -}} +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: {{ $fullName }} labels: - app.kubernetes.io/name: {{ include "fusionauth.name" . }} - helm.sh/chart: {{ include "fusionauth.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- include "fusionauth.labels" . | nindent 4 }} {{- with .Values.ingress.annotations }} annotations: {{- toYaml . | nindent 4 }} @@ -39,19 +42,14 @@ spec: {{- end }} {{- range $ingressPaths }} - path: {{ or .path . | quote }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + {{- if .pathType }} pathType: {{ .pathType | quote }} {{- end }} backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} service: name: {{ $fullName }} port: name: http - {{- else }} - serviceName: {{ $fullName }} - servicePort: http - {{- end }} {{- end }} {{- end }} {{- end }} diff --git a/chart/templates/poddisruptionbudget.yaml b/chart/templates/poddisruptionbudget.yaml index 8cacef0..36773d7 100644 --- a/chart/templates/poddisruptionbudget.yaml +++ b/chart/templates/poddisruptionbudget.yaml @@ -1,11 +1,24 @@ {{- if .Values.podDisruptionBudget.enabled -}} -apiVersion: {{ include "fusionauth.PodDisruptionBudget" . }} +{{- if and (ne .Values.podDisruptionBudget.minAvailable nil) (ne .Values.podDisruptionBudget.maxUnavailable nil) }} +{{- fail "podDisruptionBudget.minAvailable and podDisruptionBudget.maxUnavailable cannot both be set" }} +{{- end }} +apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: {{ include "fusionauth.fullname" . }} + labels: + {{- include "fusionauth.labels" . | nindent 4 }} spec: - maxUnavailable: {{ sub .Values.replicaCount 1 }} + {{- if ne .Values.podDisruptionBudget.minAvailable nil }} + {{- $minAvailable := .Values.podDisruptionBudget.minAvailable }} + minAvailable: {{ if kindIs "string" $minAvailable }}{{ $minAvailable | quote }}{{ else }}{{ $minAvailable }}{{ end }} + {{- else if ne .Values.podDisruptionBudget.maxUnavailable nil }} + {{- $maxUnavailable := .Values.podDisruptionBudget.maxUnavailable }} + maxUnavailable: {{ if kindIs "string" $maxUnavailable }}{{ $maxUnavailable | quote }}{{ else }}{{ $maxUnavailable }}{{ end }} + {{- else }} + maxUnavailable: {{ max 0 (sub .Values.replicaCount 1) }} + {{- end }} selector: matchLabels: - app.kubernetes.io/name: {{ include "fusionauth.name" . }} -{{- end }} \ No newline at end of file + {{- include "fusionauth.selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/chart/templates/secret.yaml b/chart/templates/secret.yaml index 624c064..9ff1a6f 100644 --- a/chart/templates/secret.yaml +++ b/chart/templates/secret.yaml @@ -1,17 +1,34 @@ -{{- if eq .Values.database.existingSecret "" -}} +{{- include "fusionauth.database.validate" . }} +{{- $dbUserGeneratedSecretEnabled := eq (include "fusionauth.database.dbUser.generatedSecret.enabled" .) "true" -}} +{{- $rootUserGeneratedSecretEnabled := eq (include "fusionauth.database.rootUser.generatedSecret.enabled" .) "true" -}} +{{- if $dbUserGeneratedSecretEnabled -}} +{{- if not (include "fusionauth.database.dbUser.password" .) -}} +{{- fail "database.dbUser.password is required when database.dbUser.existingSecret.enabled is false; legacy database.password is also accepted" }} +{{- end -}} apiVersion: v1 data: - password: {{ required "A password for your database is required!" .Values.database.password | b64enc }} -{{- if .Values.database.root.password }} - rootpassword: {{ .Values.database.root.password | b64enc }} + password: {{ include "fusionauth.database.dbUser.password" . | b64enc }} +kind: Secret +metadata: + labels: + {{- include "fusionauth.labels" . | nindent 4 }} + name: {{ include "fusionauth.database.dbUser.secretName" . }} +type: Opaque {{- end }} +{{ if $rootUserGeneratedSecretEnabled }} +{{ if $dbUserGeneratedSecretEnabled }} +--- +{{ end }} +{{- if not (include "fusionauth.database.rootUser.password" .) -}} +{{- fail "database.rootUser.password is required when database.rootUser.username is set and database.rootUser.existingSecret.enabled is false; legacy database.root.password is also accepted" }} +{{- end -}} +apiVersion: v1 +data: + password: {{ include "fusionauth.database.rootUser.password" . | b64enc }} kind: Secret metadata: labels: - app.kubernetes.io/name: {{ include "fusionauth.name" . }} - helm.sh/chart: {{ include "fusionauth.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - name: {{ include "fusionauth.database.secretName" . }} + {{- include "fusionauth.labels" . | nindent 4 }} + name: {{ include "fusionauth.database.rootUser.secretName" . }} type: Opaque -{{- end -}} \ No newline at end of file +{{- end -}} diff --git a/chart/templates/service.yaml b/chart/templates/service.yaml index 850e93f..ca9c792 100644 --- a/chart/templates/service.yaml +++ b/chart/templates/service.yaml @@ -3,13 +3,10 @@ kind: Service metadata: name: {{ include "fusionauth.fullname" . }} labels: - app.kubernetes.io/name: {{ include "fusionauth.name" . }} - helm.sh/chart: {{ include "fusionauth.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - {{- if .Values.service.annotations }} + {{- include "fusionauth.labels" . | nindent 4 }} + {{- with .Values.service.annotations }} annotations: -{{ .Values.service.annotations | toYaml | indent 4 }} + {{- toYaml . | nindent 4 }} {{- end }} spec: type: {{ .Values.service.type }} @@ -19,8 +16,4 @@ spec: protocol: TCP name: http selector: - app.kubernetes.io/name: {{ include "fusionauth.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - {{- if .Values.service.spec }} -{{ .Values.service.spec | toYaml | indent 2 }} - {{- end }} + {{- include "fusionauth.selectorLabels" . | nindent 4 }} diff --git a/chart/templates/serviceaccount.yaml b/chart/templates/serviceaccount.yaml index eee3271..4d6ac79 100644 --- a/chart/templates/serviceaccount.yaml +++ b/chart/templates/serviceaccount.yaml @@ -3,6 +3,8 @@ apiVersion: v1 kind: ServiceAccount metadata: name: {{ include "fusionauth.serviceAccountName" . }} + labels: + {{- include "fusionauth.labels" . | nindent 4 }} {{- with .Values.serviceAccount.annotations }} annotations: {{- toYaml . | nindent 4 }} diff --git a/chart/templates/servicemonitor.yaml b/chart/templates/servicemonitor.yaml index d33e287..9db06b7 100644 --- a/chart/templates/servicemonitor.yaml +++ b/chart/templates/servicemonitor.yaml @@ -8,10 +8,7 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} labels: - app.kubernetes.io/name: {{ include "fusionauth.name" . }} - helm.sh/chart: {{ include "fusionauth.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- include "fusionauth.labels" . | nindent 4 }} {{- with .Values.serviceMonitor.labels }} {{- toYaml . | nindent 4 }} {{- end }} @@ -22,10 +19,7 @@ spec: {{- end }} selector: matchLabels: - app.kubernetes.io/name: {{ include "fusionauth.name" . }} - helm.sh/chart: {{ include "fusionauth.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- include "fusionauth.selectorLabels" . | nindent 6 }} endpoints: - port: http {{- with .Values.serviceMonitor.path }} diff --git a/chart/templates/tests/test-connection.yaml b/chart/templates/tests/test-connection.yaml deleted file mode 100644 index 67b02c7..0000000 --- a/chart/templates/tests/test-connection.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{ include "fusionauth.fullname" . }}-test-connection" - labels: - app.kubernetes.io/name: {{ include "fusionauth.name" . }} - helm.sh/chart: {{ include "fusionauth.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - annotations: - "helm.sh/hook": test-success -spec: - containers: - - name: wget - image: "{{ .Values.initContainers.image.repository }}:{{ .Values.initContainers.image.tag }}" - command: ['wget'] - args: ['{{ include "fusionauth.fullname" . }}:{{ .Values.service.port }}'] - restartPolicy: Never diff --git a/chart/tests/database_test.yaml b/chart/tests/database_test.yaml new file mode 100644 index 0000000..ce2eae2 --- /dev/null +++ b/chart/tests/database_test.yaml @@ -0,0 +1,524 @@ +suite: database credential matrix +templates: + - secret.yaml + - deployment.yaml +release: + name: test +tests: + - it: renders database.url as DATABASE_URL while keeping wait host from database.host + set: + database.url: jdbc:postgresql://db-primary:5432/fusionauth?sslmode=verify-full&connectTimeout=10 + database.host: db-proxy + database.port: 15432 + database.dbUser.username: db_user + database.dbUser.password: db_pass + search.engine: database + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_URL + value: jdbc:postgresql://db-primary:5432/fusionauth?sslmode=verify-full&connectTimeout=10 + template: deployment.yaml + - matchRegex: + path: spec.template.spec.initContainers[0].args[2] + pattern: "nc -zv 'db-proxy' 15432" + template: deployment.yaml + + - it: renders custom environment entries before chart-managed entries + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.password: db_pass + search.engine: database + environment: + - name: SOME_VAR + value: some_value + asserts: + - equal: + path: spec.template.spec.containers[0].env[0] + value: + name: SOME_VAR + value: some_value + template: deployment.yaml + - equal: + path: spec.template.spec.containers[0].env[1] + value: + name: DATABASE_USERNAME + value: db_user + template: deployment.yaml + + - it: renders legacy inline database credentials without root credentials + set: + database.host: db + database.user: legacy_user + database.password: legacy_pass + search.engine: database + asserts: + - hasDocuments: + count: 1 + template: secret.yaml + - equal: + path: metadata.name + value: test-db-credentials + template: secret.yaml + - equal: + path: data.password + value: bGVnYWN5X3Bhc3M= + template: secret.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_USERNAME + value: legacy_user + template: deployment.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: test-db-credentials + key: password + template: deployment.yaml + - notContains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_ROOT_USERNAME + value: legacy_root + template: deployment.yaml + - notContains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: test-db-root-credentials + key: password + template: deployment.yaml + + - it: renders legacy inline database credentials with root credentials + set: + database.host: db + database.user: legacy_user + database.password: legacy_pass + database.root.user: legacy_root + database.root.password: legacy_root_pass + search.engine: database + asserts: + - hasDocuments: + count: 2 + template: secret.yaml + - equal: + path: metadata.name + value: test-db-credentials + documentIndex: 0 + template: secret.yaml + - equal: + path: data.password + value: bGVnYWN5X3Bhc3M= + documentIndex: 0 + template: secret.yaml + - equal: + path: metadata.name + value: test-db-root-credentials + documentIndex: 1 + template: secret.yaml + - equal: + path: data.password + value: bGVnYWN5X3Jvb3RfcGFzcw== + documentIndex: 1 + template: secret.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_USERNAME + value: legacy_user + template: deployment.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: test-db-credentials + key: password + template: deployment.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_ROOT_USERNAME + value: legacy_root + template: deployment.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: test-db-root-credentials + key: password + template: deployment.yaml + + - it: uses legacy existing database secret for database and root credentials + set: + database.host: db + database.user: legacy_user + database.existingSecret: legacy-db-creds + database.root.user: legacy_root + search.engine: database + asserts: + - hasDocuments: + count: 0 + template: secret.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_USERNAME + value: legacy_user + template: deployment.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: legacy-db-creds + key: password + template: deployment.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_ROOT_USERNAME + value: legacy_root + template: deployment.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: legacy-db-creds + key: rootpassword + template: deployment.yaml + + - it: uses legacy existing database secret without root credentials + set: + database.host: db + database.user: legacy_user + database.existingSecret: legacy-db-creds + search.engine: database + asserts: + - hasDocuments: + count: 0 + template: secret.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_USERNAME + value: legacy_user + template: deployment.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: legacy-db-creds + key: password + template: deployment.yaml + - notContains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_ROOT_USERNAME + value: legacy_root + template: deployment.yaml + + - it: renders new inline database credentials without root credentials + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.password: db_pass + search.engine: database + asserts: + - hasDocuments: + count: 1 + template: secret.yaml + - equal: + path: metadata.name + value: test-db-credentials + template: secret.yaml + - equal: + path: data.password + value: ZGJfcGFzcw== + template: secret.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_USERNAME + value: db_user + template: deployment.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: test-db-credentials + key: password + template: deployment.yaml + - notContains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_ROOT_USERNAME + value: root_user + template: deployment.yaml + + - it: renders new inline database credentials with root credentials + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.password: db_pass + database.rootUser.username: root_user + database.rootUser.password: root_pass + search.engine: database + asserts: + - hasDocuments: + count: 2 + template: secret.yaml + - equal: + path: metadata.name + value: test-db-credentials + documentIndex: 0 + template: secret.yaml + - equal: + path: data.password + value: ZGJfcGFzcw== + documentIndex: 0 + template: secret.yaml + - equal: + path: metadata.name + value: test-db-root-credentials + documentIndex: 1 + template: secret.yaml + - equal: + path: data.password + value: cm9vdF9wYXNz + documentIndex: 1 + template: secret.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_USERNAME + value: db_user + template: deployment.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: test-db-credentials + key: password + template: deployment.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_ROOT_USERNAME + value: root_user + template: deployment.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: test-db-root-credentials + key: password + template: deployment.yaml + + - it: uses a new existing secret for database credentials + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.existingSecret.enabled: true + database.dbUser.existingSecret.name: db-creds + database.dbUser.existingSecret.passwordKey: db-password + search.engine: database + asserts: + - hasDocuments: + count: 0 + template: secret.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_USERNAME + value: db_user + template: deployment.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: db-creds + key: db-password + template: deployment.yaml + - notContains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_ROOT_USERNAME + value: root_user + template: deployment.yaml + + - it: uses separate new existing secrets for database and root credentials + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.existingSecret.enabled: true + database.dbUser.existingSecret.name: db-creds + database.dbUser.existingSecret.passwordKey: db-password + database.rootUser.username: root_user + database.rootUser.existingSecret.enabled: true + database.rootUser.existingSecret.name: root-creds + database.rootUser.existingSecret.passwordKey: root-password + search.engine: database + asserts: + - hasDocuments: + count: 0 + template: secret.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: db-creds + key: db-password + template: deployment.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: root-creds + key: root-password + template: deployment.yaml + + - it: uses existing database secret with generated root secret + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.existingSecret.enabled: true + database.dbUser.existingSecret.name: db-creds + database.dbUser.existingSecret.passwordKey: db-password + database.rootUser.username: root_user + database.rootUser.password: root_pass + search.engine: database + asserts: + - hasDocuments: + count: 1 + template: secret.yaml + - equal: + path: metadata.name + value: test-db-root-credentials + template: secret.yaml + - equal: + path: data.password + value: cm9vdF9wYXNz + template: secret.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: db-creds + key: db-password + template: deployment.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: test-db-root-credentials + key: password + template: deployment.yaml + + - it: uses generated database secret with existing root secret + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.password: db_pass + database.rootUser.username: root_user + database.rootUser.existingSecret.enabled: true + database.rootUser.existingSecret.name: root-creds + database.rootUser.existingSecret.passwordKey: root-password + search.engine: database + asserts: + - hasDocuments: + count: 1 + template: secret.yaml + - equal: + path: metadata.name + value: test-db-credentials + template: secret.yaml + - equal: + path: data.password + value: ZGJfcGFzcw== + template: secret.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: test-db-credentials + key: password + template: deployment.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: root-creds + key: root-password + template: deployment.yaml + + - it: uses shared new existing database secret when keys are different + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.existingSecret.enabled: true + database.dbUser.existingSecret.name: shared-db-creds + database.dbUser.existingSecret.passwordKey: password + database.rootUser.username: root_user + database.rootUser.existingSecret.enabled: true + database.rootUser.existingSecret.name: shared-db-creds + database.rootUser.existingSecret.passwordKey: rootpassword + search.engine: database + asserts: + - hasDocuments: + count: 0 + template: secret.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: shared-db-creds + key: password + template: deployment.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: shared-db-creds + key: rootpassword + template: deployment.yaml diff --git a/chart/tests/init_test.yaml b/chart/tests/init_test.yaml new file mode 100644 index 0000000..5057533 --- /dev/null +++ b/chart/tests/init_test.yaml @@ -0,0 +1,63 @@ +suite: init container matrix +templates: + - deployment.yaml +release: + name: test +tests: + - it: renders default database and search wait containers + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.password: db_pass + search.engine: elasticsearch + search.host: search + asserts: + - equal: + path: spec.template.spec.initContainers[0].name + value: wait-for-db + - equal: + path: spec.template.spec.initContainers[1].name + value: wait-for-search + + - it: disables database wait using waitForDb + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.password: db_pass + search.engine: database + initContainers.waitForDb: false + asserts: + - notExists: + path: spec.template.spec.initContainers + + - it: disables search wait using legacy waitForEs + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.password: db_pass + search.engine: elasticsearch + search.host: search + initContainers.waitForEs: false + asserts: + - equal: + path: spec.template.spec.initContainers[0].name + value: wait-for-db + - lengthEqual: + path: spec.template.spec.initContainers + count: 1 + + - it: disables search wait using waitForSearch + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.password: db_pass + search.engine: elasticsearch + search.host: search + initContainers.waitForSearch: false + asserts: + - equal: + path: spec.template.spec.initContainers[0].name + value: wait-for-db + - lengthEqual: + path: spec.template.spec.initContainers + count: 1 diff --git a/chart/tests/negative_test.yaml b/chart/tests/negative_test.yaml new file mode 100644 index 0000000..d233ec6 --- /dev/null +++ b/chart/tests/negative_test.yaml @@ -0,0 +1,139 @@ +suite: validation matrix +templates: + - secret.yaml + - deployment.yaml +release: + name: test +tests: + - it: fails when new database and root existing secrets use the same Secret and key + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.existingSecret.enabled: true + database.dbUser.existingSecret.name: shared-db-creds + database.dbUser.existingSecret.passwordKey: password + database.rootUser.username: root_user + database.rootUser.existingSecret.enabled: true + database.rootUser.existingSecret.name: shared-db-creds + database.rootUser.existingSecret.passwordKey: password + search.engine: database + asserts: + - failedTemplate: + errorMessage: passwordKey values must be different when database.dbUser.existingSecret.name and database.rootUser.existingSecret.name reference the same Secret + template: secret.yaml + + - it: fails when legacy database password is set without legacy database user + set: + database.host: db + database.password: legacy_pass + database.dbUser.username: "" + search.engine: database + asserts: + - failedTemplate: + errorMessage: database.user is required when database.password is set + template: secret.yaml + + - it: fails when new database password is set without new database username + set: + database.host: db + database.dbUser.username: "" + database.dbUser.password: db_pass + search.engine: database + asserts: + - failedTemplate: + errorMessage: database.dbUser.username is required when database.dbUser.password is set + template: secret.yaml + + - it: fails when legacy root password is set without legacy root user + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.password: db_pass + database.root.password: root_pass + database.rootUser.username: "" + search.engine: database + asserts: + - failedTemplate: + errorMessage: database.root.user is required when database.root.password is set + template: secret.yaml + + - it: fails when new root password is set without new root username + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.password: db_pass + database.rootUser.username: "" + database.rootUser.password: root_pass + search.engine: database + asserts: + - failedTemplate: + errorMessage: database.rootUser.username is required when database.rootUser.password is set + template: secret.yaml + + - it: fails when environment overrides DATABASE_URL + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.password: db_pass + search.engine: database + environment: + - name: DATABASE_URL + value: jdbc:postgresql://db:5432/fusionauth + asserts: + - failedTemplate: + errorMessage: environment cannot override chart-managed variable DATABASE_URL; use the corresponding chart value instead + template: secret.yaml + + - it: fails when database.url is set without a wait host + set: + database.url: jdbc:postgresql://db:5432/fusionauth?sslmode=verify-full + database.dbUser.username: db_user + database.dbUser.password: db_pass + search.engine: database + asserts: + - failedTemplate: + errorMessage: database.host is required when database.url is set and initContainers.waitForDb is true + template: secret.yaml + + - it: fails when environment overrides SEARCH_SERVERS + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.password: db_pass + search.engine: elasticsearch + search.host: search + environment: + - name: SEARCH_SERVERS + value: http://search:9200 + asserts: + - failedTemplate: + errorMessage: environment cannot override chart-managed variable SEARCH_SERVERS; use the corresponding chart value instead + template: secret.yaml + + - it: fails when search basic auth is enabled without a username + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.password: db_pass + search.engine: elasticsearch + search.host: search + search.basicAuth.enabled: true + search.basicAuth.password: search_pass + asserts: + - failedTemplate: + errorMessage: search.basicAuth.username is required when search.basicAuth.enabled is true and search.basicAuth.existingSecret.enabled is false + template: deployment.yaml + + - it: fails when search basic auth is enabled without a password + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.password: db_pass + search.engine: elasticsearch + search.host: search + search.basicAuth.enabled: true + search.basicAuth.username: search_user + asserts: + - failedTemplate: + errorMessage: search.basicAuth.password is required when search.basicAuth.enabled is true and search.basicAuth.existingSecret.enabled is false + template: deployment.yaml diff --git a/chart/tests/search_test.yaml b/chart/tests/search_test.yaml new file mode 100644 index 0000000..4913f3b --- /dev/null +++ b/chart/tests/search_test.yaml @@ -0,0 +1,138 @@ +suite: search credential matrix +templates: + - deployment.yaml +release: + name: test +tests: + - it: renders search without basic auth + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.password: db_pass + search.engine: elasticsearch + search.host: search + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: SEARCH_TYPE + value: elasticsearch + - contains: + path: spec.template.spec.containers[0].env + content: + name: SEARCH_SERVERS + value: http://search:9200 + - notContains: + path: spec.template.spec.containers[0].env + content: + name: SEARCH_USERNAME + value: search_user + - notContains: + path: spec.template.spec.containers[0].env + content: + name: SEARCH_PASSWORD + value: search_pass + + - it: renders legacy search inline credentials in SEARCH_SERVERS + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.password: db_pass + search.engine: elasticsearch + search.host: search + search.user: legacy_search_user + search.password: legacy_search_pass + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: SEARCH_SERVERS + value: http://legacy_search_user:legacy_search_pass@search:9200 + - notContains: + path: spec.template.spec.containers[0].env + content: + name: SEARCH_USERNAME + value: legacy_search_user + + - it: renders new search inline credentials in SEARCH_SERVERS + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.password: db_pass + search.engine: elasticsearch + search.host: search + search.basicAuth.enabled: true + search.basicAuth.username: search_user + search.basicAuth.password: search_pass + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: SEARCH_SERVERS + value: http://search_user:search_pass@search:9200 + - notContains: + path: spec.template.spec.containers[0].env + content: + name: SEARCH_USERNAME + value: search_user + + - it: uses new existing search secret with custom keys + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.password: db_pass + search.engine: elasticsearch + search.host: search + search.basicAuth.existingSecret.enabled: true + search.basicAuth.existingSecret.name: search-creds + search.basicAuth.existingSecret.userKey: search-username + search.basicAuth.existingSecret.passwordKey: search-password + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: SEARCH_USERNAME + valueFrom: + secretKeyRef: + name: search-creds + key: search-username + - contains: + path: spec.template.spec.containers[0].env + content: + name: SEARCH_PASSWORD + valueFrom: + secretKeyRef: + name: search-creds + key: search-password + - contains: + path: spec.template.spec.containers[0].env + content: + name: SEARCH_SERVERS + value: http://$(SEARCH_USERNAME):$(SEARCH_PASSWORD)@search:9200 + + - it: uses new existing search secret with default keys + set: + database.host: db + database.dbUser.username: db_user + database.dbUser.password: db_pass + search.engine: elasticsearch + search.host: search + search.basicAuth.existingSecret.enabled: true + search.basicAuth.existingSecret.name: search-creds + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: SEARCH_USERNAME + valueFrom: + secretKeyRef: + name: search-creds + key: username + - contains: + path: spec.template.spec.containers[0].env + content: + name: SEARCH_PASSWORD + valueFrom: + secretKeyRef: + name: search-creds + key: password diff --git a/chart/values.schema.json b/chart/values.schema.json index 1f81d39..b5bdc54 100644 --- a/chart/values.schema.json +++ b/chart/values.schema.json @@ -15,7 +15,11 @@ "type": "string" }, "runtimeMode": { - "type": "string" + "type": "string", + "enum": [ + "development", + "production" + ] }, "silentMode": { "type": "boolean" @@ -27,12 +31,59 @@ "properties": { "enabled": { "type": "boolean" + }, + "maxReplicas": { + "type": "integer", + "minimum": 1 + }, + "minReplicas": { + "type": "integer", + "minimum": 1 + }, + "targetCPU": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + }, + "targetMemory": { + "type": [ + "integer", + "null" + ], + "minimum": 1 } } }, "database": { "type": "object", "properties": { + "dbUser": { + "type": "object", + "properties": { + "existingSecret": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "passwordKey": { + "type": "string" + } + } + }, + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, "existingSecret": { "type": "string" }, @@ -46,11 +97,20 @@ "type": "string" }, "port": { - "type": "integer" + "type": "integer", + "minimum": 1, + "maximum": 65535 }, - "protocol": { + "url": { "type": "string" }, + "protocol": { + "type": "string", + "enum": [ + "postgresql", + "mysql" + ] + }, "root": { "type": "object", "properties": { @@ -62,6 +122,31 @@ } } }, + "rootUser": { + "type": "object", + "properties": { + "existingSecret": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "passwordKey": { + "type": "string" + } + } + }, + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, "tls": { "type": "boolean" }, @@ -77,22 +162,92 @@ "type": "object" }, "dnsPolicy": { - "type": "string" + "type": "string", + "enum": [ + "ClusterFirst", + "ClusterFirstWithHostNet", + "Default", + "None" + ] }, "environment": { - "type": "array" + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + }, + "valueFrom": { + "type": "object" + } + }, + "required": [ + "name" + ], + "oneOf": [ + { + "required": [ + "value" + ], + "not": { + "required": [ + "valueFrom" + ] + } + }, + { + "required": [ + "valueFrom" + ], + "not": { + "required": [ + "value" + ] + } + } + ] + } }, "extraContainers": { - "type": "array" + "type": "array", + "items": { + "type": "object", + "required": [ + "name" + ] + } }, "extraInitContainers": { - "type": "array" + "type": "array", + "items": { + "type": "object", + "required": [ + "name" + ] + } }, "extraVolumeMounts": { - "type": "array" + "type": "array", + "items": { + "type": "object", + "required": [ + "name", + "mountPath" + ] + } }, "extraVolumes": { - "type": "array" + "type": "array", + "items": { + "type": "object", + "required": [ + "name" + ] + } }, "fullnameOverride": { "type": "string" @@ -101,7 +256,12 @@ "type": "object", "properties": { "pullPolicy": { - "type": "string" + "type": "string", + "enum": [ + "Always", + "IfNotPresent", + "Never" + ] }, "repository": { "type": "string" @@ -127,13 +287,56 @@ "type": "array" }, "hosts": { - "type": "array" + "type": "array", + "items": { + "type": "string" + } + }, + "ingressClassName": { + "type": [ + "string", + "null" + ] }, "paths": { - "type": "array" + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "pathType": { + "type": "string", + "enum": [ + "Exact", + "ImplementationSpecific", + "Prefix" + ] + } + }, + "required": [ + "path", + "pathType" + ] + } }, "tls": { - "type": "array" + "type": "array", + "items": { + "type": "object", + "properties": { + "hosts": { + "type": "array", + "items": { + "type": "string" + } + }, + "secretName": { + "type": "string" + } + } + } } } }, @@ -159,6 +362,9 @@ }, "waitForEs": { "type": "boolean" + }, + "waitForSearch": { + "type": "boolean" } } }, @@ -166,10 +372,17 @@ "type": "object", "properties": { "data": { - "type": "object" + "type": "object", + "additionalProperties": { + "type": "string" + } }, "enabled": { "type": "boolean" + }, + "file": { + "type": "string", + "pattern": "^/" } } }, @@ -180,7 +393,8 @@ "type": "object", "properties": { "failureThreshold": { - "type": "integer" + "type": "integer", + "minimum": 1 }, "httpGet": { "type": "object", @@ -189,15 +403,36 @@ "type": "string" }, "port": { - "type": "string" + "type": [ + "integer", + "string" + ] + }, + "scheme": { + "type": "string", + "enum": [ + "HTTP", + "HTTPS" + ] } } }, + "initialDelaySeconds": { + "type": "integer", + "minimum": 0 + }, "periodSeconds": { - "type": "integer" + "type": "integer", + "minimum": 1 + }, + "successThreshold": { + "type": "integer", + "minimum": 1, + "maximum": 1 }, "timeoutSeconds": { - "type": "integer" + "type": "integer", + "minimum": 1 } } }, @@ -218,14 +453,62 @@ "properties": { "enabled": { "type": "boolean" + }, + "maxUnavailable": { + "oneOf": [ + { + "type": "integer", + "minimum": 0 + }, + { + "type": "string", + "pattern": "^[0-9]+%$" + }, + { + "type": "null" + } + ] + }, + "minAvailable": { + "oneOf": [ + { + "type": "integer", + "minimum": 0 + }, + { + "type": "string", + "pattern": "^[0-9]+%$" + }, + { + "type": "null" + } + ] } } }, + "podLabels": { + "type": "object", + "not": { + "anyOf": [ + { + "required": [ + "app.kubernetes.io/name" + ] + }, + { + "required": [ + "app.kubernetes.io/instance" + ] + } + ] + } + }, "readinessProbe": { "type": "object", "properties": { "failureThreshold": { - "type": "integer" + "type": "integer", + "minimum": 1 }, "httpGet": { "type": "object", @@ -234,17 +517,41 @@ "type": "string" }, "port": { - "type": "string" + "type": [ + "integer", + "string" + ] + }, + "scheme": { + "type": "string", + "enum": [ + "HTTP", + "HTTPS" + ] } } }, + "initialDelaySeconds": { + "type": "integer", + "minimum": 0 + }, + "periodSeconds": { + "type": "integer", + "minimum": 1 + }, + "successThreshold": { + "type": "integer", + "minimum": 1 + }, "timeoutSeconds": { - "type": "integer" + "type": "integer", + "minimum": 1 } } }, "replicaCount": { - "type": "integer" + "type": "integer", + "minimum": 0 }, "resources": { "type": "object" @@ -254,43 +561,155 @@ }, "search": { "type": "object", + "additionalProperties": false, "properties": { + "basicAuth": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "existingSecret": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "passwordKey": { + "type": "string" + }, + "userKey": { + "type": "string" + } + } + }, + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, "engine": { - "type": "string" + "type": "string", + "enum": [ + "elasticsearch", + "database" + ] }, "host": { "type": "string" }, + "password": { + "type": "string" + }, "port": { - "type": "integer" + "type": "integer", + "minimum": 1, + "maximum": 65535 }, "protocol": { + "type": "string", + "enum": [ + "http", + "https" + ] + }, + "user": { "type": "string" } } }, "service": { "type": "object", + "additionalProperties": false, "properties": { "annotations": { "type": "object" }, "port": { - "type": "integer" + "type": "integer", + "minimum": 1, + "maximum": 65535 }, - "spec": { + "type": { + "type": "string", + "enum": [ + "ClusterIP", + "NodePort", + "LoadBalancer" + ] + } + } + }, + "serviceAccount": { + "type": "object", + "properties": { + "annotations": { "type": "object" }, - "type": { + "automount": { + "type": "boolean" + }, + "create": { + "type": "boolean" + }, + "name": { "type": "string" } } }, + "serviceMonitor": { + "type": "object", + "properties": { + "annotations": { + "type": "object" + }, + "basicAuth": { + "type": "object" + }, + "enabled": { + "type": "boolean" + }, + "interval": { + "type": [ + "string", + "null" + ] + }, + "labels": { + "type": "object" + }, + "namespaceSelector": { + "type": "object" + }, + "path": { + "type": "string", + "pattern": "^/" + }, + "relabelings": { + "type": "array" + }, + "scrapeTimeout": { + "type": [ + "string", + "null" + ] + } + } + }, "startupProbe": { "type": "object", "properties": { "failureThreshold": { - "type": "integer" + "type": "integer", + "minimum": 1 }, "httpGet": { "type": "object", @@ -299,15 +718,36 @@ "type": "string" }, "port": { - "type": "string" + "type": [ + "integer", + "string" + ] + }, + "scheme": { + "type": "string", + "enum": [ + "HTTP", + "HTTPS" + ] } } }, + "initialDelaySeconds": { + "type": "integer", + "minimum": 0 + }, "periodSeconds": { - "type": "integer" + "type": "integer", + "minimum": 1 + }, + "successThreshold": { + "type": "integer", + "minimum": 1, + "maximum": 1 }, "timeoutSeconds": { - "type": "integer" + "type": "integer", + "minimum": 1 } } }, @@ -318,4 +758,4 @@ "type": "array" } } -} \ No newline at end of file +} diff --git a/chart/values.yaml b/chart/values.yaml index 7e72900..a8a8778 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -1,15 +1,14 @@ # Default values for fusionauth. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. # replicaCount -- The number of fusionauth-app instances to run replicaCount: 1 +# image -- Configures the docker image to use for fusionauth-app image: # image.repository -- The name of the docker repository for fusionauth-app repository: docker.io/fusionauth/fusionauth-app # image.tag -- The docker tag to pull for fusionauth-app - tag: ${APP_VERSION} + tag: 0.0.0-app-dev # image.pullPolicy -- Kubernetes image pullPolicy to use for fusionauth-app pullPolicy: IfNotPresent @@ -20,26 +19,20 @@ nameOverride: "" # fullnameOverride -- Overrides full resource names fullnameOverride: "" -# - spec for Container: -# kubectl explain pod.spec.initContainers -# kubectl explain pod.spec.initContainers --recursive -extraInitContainers: [] - +# initContainers -- Configures init containers for fusionauth pods. Init containers are used to wait for the database and search engine to be ready before starting fusionauth. initContainers: + # initContainers.waitForDb -- waits for the database to be ready. Setting this to `false` is not recommended. waitForDb: true - waitForEs: true - # This image should contain `nc`, `wget` and a shell of some kind to do a simple loop. + # initContainers.waitForSearch -- waits for the search engine to be ready. Setting this to `false` is not recommended. + waitForSearch: true + # initContainers.image -- Configures the docker image to use for init containers. image: - # initContainers.image.repository -- Docker image to use for initContainers + # initContainers.image.repository -- Docker image to use for initContainers. This image must contain `nc`, `wget` and a shell of some kind to do a simple loop. repository: docker.io/library/busybox # initContainers.image.tag -- Tag to use for initContainers docker image tag: 1.36.1 - # initContainers.resources -- Resource requests and limits to use for initContainers + # initContainers.resources -- It is recommended to set these values when you understand FusionAuth's resource usage in your specific environment. resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. # limits: # cpu: 100m # memory: 128Mi @@ -47,11 +40,13 @@ initContainers: # cpu: 100m # memory: 128Mi -# - spec for Container: -# kubectl explain pod.spec.containers -# kubectl explain pod.spec.containers --recursive +# extraInitContainers -- Add specs for additional init containers if needed. +extraInitContainers: [] + +# extraContainers -- Add specs for additional containers if needed. extraContainers: [] +# service -- Configures the Kubernetes service for FusionAuth. service: # service.type -- Type of Kubernetes service to create type: ClusterIP @@ -59,16 +54,17 @@ service: port: 9011 # service.annotations -- Extra annotations to add to service object annotations: {} - # service.spec -- Any extra fields to add to the service object spec - spec: {} +# database -- Configures the database connection for fusionauth database: - # database.protocol -- Should either be postgresql or mysql. Protocol for jdbc connection to database - protocol: postgresql - # database.host -- Hostname or ip of the database instance + # database.url -- Optional full JDBC URL. When set, this value is used for DATABASE_URL instead of building it from protocol, host, port, name, tls, and tlsMode. + url: "" + # database.host -- Hostname or ip of the database instance. Required by the wait-for-db init container even when database.url is set. host: "" - # database.host -- Port of the database instance + # database.port -- Port of the database instance. Required by the wait-for-db init container even when database.url is set. port: 5432 + # database.protocol -- Protocol for jdbc connection to database [`postgresql|mysql`]. + protocol: postgresql # database.tls -- Configures whether or not to use tls when connecting to the database tls: false # database.tlsMode -- If tls is enabled, this configures the mode @@ -76,34 +72,67 @@ database: # database.name -- Name of the fusionauth database name: fusionauth - # To use an existing secret, set `existingSecret` to the name of the secret. We expect at most two keys: `password` is required. `rootpassword` is only required if `database.root.user` is set. - # database.existingSecret -- The name of an existing secret that contains the database passwords - existingSecret: "" - # database.user -- Database username for fusionauth to use in normal operation - user: "" - # database.password -- Database password for fusionauth to use in normal operation - not required if database.existingSecret is configured - password: "" - # These credentials are used for bootstrapping the database - root: - # database.root.user -- Database username for fusionauth to use during initial bootstrap - not required if you have manually bootstrapped your database - user: "" - # database.root.password -- Database password for fusionauth to use during initial bootstrap - not required if database.existingSecret is configured + # database.dbUser -- Database credentials for fusionauth to use in normal operation + dbUser: + # database.dbUser.username -- Database username for fusionauth to use in normal operation. + username: "" + # database.dbUser.password -- Database password for fusionauth to use in normal operation. + # It is not recommended to set the password in clear text here. Use an existing secret instead. password: "" - + # database.dbUser.existingSecret -- Configures an existing secret that contains the normal database user password. + existingSecret: + # database.dbUser.existingSecret.enabled -- Use an existing secret for the normal database user password. + enabled: false + # database.dbUser.existingSecret.name -- The name of an existing secret that contains the normal database user password. + name: "" + # database.dbUser.existingSecret.passwordKey -- The key in the existing secret that contains the database password. + passwordKey: password + # database.rootUser -- Database credentials for fusionauth to use during initial bootstrap + rootUser: + # database.rootUser.username -- Database username for fusionauth to use during initial bootstrap + username: "" + # database.rootUser.password -- Database password for fusionauth to use during initial bootstrap + # It is not recommended to set the password in clear text here. Use an existing secret instead. + password: "" + # database.rootUser.existingSecret -- Configures an existing secret that contains the root database user password. + existingSecret: + # database.rootUser.existingSecret.enabled -- Use an existing secret for the root database user password. + enabled: false + # database.rootUser.existingSecret.name -- The name of an existing secret that contains the root database user password. + name: "" + # database.rootUser.existingSecret.passwordKey -- The key in the existing secret that contains the root database password. + passwordKey: password + +# search -- Configures the search engine for fusionauth search: # search.engine -- Defines backend for fusionauth search capabilities. Valid values for engine are 'elasticsearch' or 'database'. engine: elasticsearch - # search.engine -- Protocol to use when connecting to elasticsearch. Ignored when search.engine is NOT elasticsearch + # search.protocol -- Protocol to use when connecting to elasticsearch. Ignored when search.engine is NOT elasticsearch protocol: http # search.host -- Hostname or ip to use when connecting to elasticsearch. Ignored when search.engine is NOT elasticsearch host: "" # search.port -- Port to use when connecting to elasticsearch. Ignored when search.engine is NOT elasticsearch port: 9200 - # search.user -- Username to use with basic auth when connecting to elasticsearch. Ignored when search.engine is NOT elasticsearch - # user: "" - # search.password -- Password to use with basic auth when connecting to elasticsearch. Ignored when search.engine is NOT elasticsearch - # password: "" - + # search.basicAuth -- Configures elasticsearch basic auth credentials. Ignored when search.engine is NOT elasticsearch. + basicAuth: + # search.basicAuth.enabled -- Enables elasticsearch basic auth using inline username/password. Not required when search.basicAuth.existingSecret.enabled is true. + enabled: false + # search.basicAuth.username -- Username to use with elasticsearch basic auth. Ignored when search.basicAuth.existingSecret.enabled is true. + username: "" + # search.basicAuth.password -- Password to use with elasticsearch basic auth. Ignored when search.basicAuth.existingSecret.enabled is true. + password: "" + # search.basicAuth.existingSecret -- Configures an existing secret that contains elasticsearch basic auth credentials. + existingSecret: + # search.basicAuth.existingSecret.enabled -- Use an existing secret for elasticsearch basic auth credentials. + enabled: false + # search.basicAuth.existingSecret.name -- The name of an existing secret that contains elasticsearch basic auth credentials. + name: "" + # search.basicAuth.existingSecret.userKey -- The key in search.basicAuth.existingSecret.name that contains the elasticsearch username. + userKey: username + # search.basicAuth.existingSecret.passwordKey -- The key in search.basicAuth.existingSecret.name that contains the elasticsearch password. + passwordKey: password + +# app -- Configures general settings for the fusionauth application app: # app.memory -- Configures the amount of memory Java can use memory: 256M @@ -115,7 +144,7 @@ app: # silent-mode minimizes downtime during upgrades: https://fusionauth.io/docs/operate/deploy/upgrade#downtime-and-database-migrations silentMode: false -# environment - Configure additional environment variables. Should only be used for things that are not explicitly set elsewhere in the chart. +# environment -- Configure additional environment variables. Should only be used for things that are not explicitly set elsewhere in the chart. environment: [] # - name: POD_IP # valueFrom: @@ -123,15 +152,15 @@ environment: [] # fieldPath: status.podIP # - name: FUSIONAUTH_API_KEY # value: test - # Its important to add /kickstart/ as prefix to your kickstart file else it won't work! All other files will be mounted below /kickstart/ - # => Use this environment variable to override the default location '/kickstart/kickstart.json' which is autom. set when kickstart.enabled is set - # - name: FUSIONAUTH_APP_KICKSTART_FILE - # value: /kickstart/kickstart.json +# kickstart -- Configures kickstart for initial application setup kickstart: + # kickstart.enabled -- Enable kickstart for initial application setup. enabled: false - data: - {} + # kickstart.file -- File path FusionAuth should use for kickstart configuration. + file: /kickstart/kickstart.json + # kickstart.data -- FusionAuth kickstart settings. + data: {} # kickstart.json: | # { # "variables": { @@ -169,6 +198,7 @@ kickstart: # setup-password.txt: | # Hallo +# lifecycle -- Define custom lifecycle settings for the deployment. lifecycle: {} # # lifecycle.postStart -- postStart lifecycle command for fusionauth container # postStart: @@ -179,10 +209,18 @@ lifecycle: {} # exec: # command: ["/bin/bash","-c","kill -3 1"] +# podDisruptionBudget -- Configures the PodDisruptionBudget for FusionAuth pods. podDisruptionBudget: # podDisruptionBudget.enabled -- Enables creation of a PodDisruptionBudget enabled: false - + # podDisruptionBudget.minAvailable -- (int/string/null) Minimum number of available pods. Cannot be used with maxUnavailable. + # @default -- null + minAvailable: null + # podDisruptionBudget.maxUnavailable -- (int/string/null) Maximum number of unavailable pods. Cannot be used with minAvailable. Defaults to replicaCount - 1. + # @default -- null + maxUnavailable: null + +# ingress -- Configures ingress for FusionAuth. ingress: # ingress.enabled -- Enables ingress creation for fusionauth. enabled: false @@ -190,10 +228,11 @@ ingress: annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" - # ingress.ingressClassName since the kubernetes.io/ingress.class annotation - # is deprecated as of networking.k8s.io/v1 or kubernetes 1.22+ + # ingress.ingressClassName -- (string/null) Specify the ingressClass to be used by the Ingress. The kubernetes.io/ingress.class annotation is deprecated as of networking.k8s.io/v1 or Kubernetes 1.22+. + # @default -- null ingressClassName: ~ # ingressClassName: nginx + # ingress.paths -- Paths to be used by the Ingress. paths: [] # - path: /* # pathType: Prefix @@ -208,12 +247,8 @@ ingress: # hosts: # - chart-example.local -# resources -- Define resource requests and limits for fusionauth-app. +# resources -- Define resource requests and limits for fusionauth-app. It is recommended to set these values when you understand FusionAuth's resource usage in your specific environment. resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. # limits: # cpu: 100m # memory: 128Mi @@ -221,11 +256,15 @@ resources: {} # cpu: 100m # memory: 128Mi -## Autoscaling parameters +# autoscaling -- Configures Horizontal Pod Autoscaling. If you enable autoscaling, you will need to also set resource requests for the corresponding targets. autoscaling: + # autoscaling.enabled -- Enable Horizontal Pod Autoscaling. enabled: false + # autoscaling.minReplicas -- Minimum number of running instances when HPA is enabled. minReplicas: 2 + # autoscaling.maxReplicas -- Maximum number of running instances when HPA is enabled. maxReplicas: 5 + # autoscaling.targetCPU -- CPU use % threshold to trigger a HPA scale up. targetCPU: 50 # targetMemory: 50 @@ -248,7 +287,7 @@ nodeSelector: {} # tolerations -- Define tolerations for kubernetes to use when scheduling fusionauth pods. tolerations: [] -# affinty -- Define affinity for kubernetes to use when scheduling fusionauth pods. +# affinity -- Define affinity for kubernetes to use when scheduling fusionauth pods. affinity: {} # topologySpreadConstraints -- Define topologySpreadConstraints for kubernetes to use @@ -270,26 +309,43 @@ podLabels: {} # livenessProbe -- Configures a livenessProbe to ensure fusionauth is running livenessProbe: + # livenessProbe.httpGet -- Configures the liveness probe HTTP endpoint. httpGet: + # livenessProbe.httpGet.path -- Path used for the liveness probe. path: / + # livenessProbe.httpGet.port -- Port used for the liveness probe. port: http + # livenessProbe.failureThreshold -- Failure threshold for the liveness probe. failureThreshold: 3 + # livenessProbe.periodSeconds -- Period in seconds between liveness probe checks. periodSeconds: 30 + # livenessProbe.timeoutSeconds -- Timeout in seconds for the liveness probe. timeoutSeconds: 5 # readinessProbe -- Configures a readinessProbe to ensure fusionauth is ready for requests readinessProbe: + # readinessProbe.httpGet -- Configures the readiness probe HTTP endpoint. httpGet: + # readinessProbe.httpGet.path -- Path used for the readiness probe. path: / + # readinessProbe.httpGet.port -- Port used for the readiness probe. port: http + # readinessProbe.failureThreshold -- Failure threshold for the readiness probe. failureThreshold: 5 + # readinessProbe.timeoutSeconds -- Timeout in seconds for the readiness probe. timeoutSeconds: 5 # startupProbe -- Configures a startupProbe to ensure fusionauth has finished starting up startupProbe: + # startupProbe.httpGet -- Configures the startup probe HTTP endpoint. httpGet: + # startupProbe.httpGet.path -- Path used for the startup probe. path: / + # startupProbe.httpGet.port -- Port used for the startup probe. port: http + # startupProbe.failureThreshold -- Failure threshold for the startup probe. failureThreshold: 20 + # startupProbe.periodSeconds -- Period in seconds between startup probe checks. periodSeconds: 10 + # startupProbe.timeoutSeconds -- Timeout in seconds for the startup probe. timeoutSeconds: 5 # extraVolumes -- Define extra Volumes. Allow to add existing claimName @@ -298,19 +354,20 @@ extraVolumes: [] # persistentVolumeClaim: # claimName: custom-css-data -# extraVolumes -- Associate mountPath for each extraVolumes +# extraVolumeMounts -- Associate mountPath for each extraVolumes extraVolumeMounts: [] # - name: custom-css-data # mountPath: /usr/local/fusionauth/fusionauth-app/web/custom +# serviceAccount -- Configures the Kubernetes service account for FusionAuth pods. serviceAccount: - # serviceAccount.create - Specifies whether a service account should be created + # serviceAccount.create -- Specifies whether a service account should be created create: false - # serviceAccount.automount - Automatically mount a ServiceAccount's API credentials? + # serviceAccount.automount -- Automatically mount a ServiceAccount's API credentials? automount: true - # serviceAccount.annotation - Annotations to add to the service account + # serviceAccount.annotations -- Annotations to add to the service account annotations: {} - # serviceAccount.name - The name of the service account to use. + # serviceAccount.name -- The name of the service account to use. # If not set and create is true, a name is generated using the fullname template name: "" @@ -328,10 +385,18 @@ serviceMonitor: # name: myBasicAuthSecret # key: user # serviceMonitor.path -- Configures path to metrics, defaults to FusionAuth's prometheus metrics API endpoint - path: api/prometheus/metrics + path: /api/prometheus/metrics + # serviceMonitor.namespaceSelector -- Namespace selector for the ServiceMonitor. namespaceSelector: {} + # serviceMonitor.annotations -- Annotations to add to the ServiceMonitor object. annotations: {} + # serviceMonitor.labels -- Labels to add to the ServiceMonitor object. labels: {} + # serviceMonitor.interval -- (string/null) Interval at which Prometheus should scrape metrics. + # @default -- null interval: null + # serviceMonitor.scrapeTimeout -- (string/null) Timeout for Prometheus metric scrapes. + # @default -- null scrapeTimeout: null + # serviceMonitor.relabelings -- Relabeling rules for the ServiceMonitor endpoint. relabelings: [] diff --git a/release-notes.md b/release-notes.md index 7e15207..5817197 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,6 +1,6 @@ ### FusionAuth Helm Chart ${CHART_VERSION} -**⚠️ The FusionAuth app version matches the chart version.** The means that, by default, upgrading the chart will also upgrade the FusionAuth app version. +**⚠️ The FusionAuth app version matches the chart version.** This means that, by default, upgrading the chart will also upgrade the FusionAuth app version. If you do not want a chart upgrade to modify the app version, set the `image.tag` value in the chart. You can set this in a custom values file, or by passing `--set image.tag=[version]` to the helm install/upgrade command, where `[version]` is the FusionAuth app version that you wish to use. diff --git a/scripts/validate-chart.sh b/scripts/validate-chart.sh new file mode 100755 index 0000000..d518507 --- /dev/null +++ b/scripts/validate-chart.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env sh +set -eu + +chart="${1:-chart}" + +helm lint "$chart" \ + --set database.host=postgres \ + --set database.dbUser.username=fusionauth \ + --set database.dbUser.password=password \ + --set search.host=opensearch + +helm template test "$chart" \ + --set database.host=postgres \ + --set database.dbUser.username=fusionauth \ + --set database.dbUser.password=password \ + --set search.engine=database \ + --set ingress.enabled=true \ + --set ingress.hosts[0]=example.com \ + --set ingress.paths[0].path=/ \ + --set ingress.paths[0].pathType=Prefix \ + --set autoscaling.enabled=true \ + --set podDisruptionBudget.enabled=true \ + --set serviceMonitor.enabled=true \ + >/dev/null