From dc21d202c762dd38999aed3214a24f3f8477bb29 Mon Sep 17 00:00:00 2001 From: Sudo-JHare Date: Thu, 17 Apr 2025 17:31:43 +1000 Subject: [PATCH] Add files via upload --- .../charts/hapi-fhir-jpaserver/Chart.lock | 9 + .../charts/hapi-fhir-jpaserver/Chart.yaml | 34 ++ .../charts/hapi-fhir-jpaserver/README.md | 150 +++++++++ .../hapi-fhir-jpaserver/README.md.gotmpl | 79 +++++ .../ci/custom-postgres-user-values.yaml | 7 + .../ci/enabled-ingress-values.yaml | 6 + .../ci/extra-config-values.yaml | 17 + .../ci/extra-volumes-values.yaml | 11 + .../hapi-fhir-jpaserver/templates/NOTES.txt | 22 ++ .../templates/_helpers.tpl | 152 +++++++++ .../templates/application-config.yaml | 11 + .../templates/deployment.yaml | 160 ++++++++++ .../templates/externaldb-secret.yaml | 11 + .../templates/ingress.yaml | 53 +++ .../templates/poddisruptionbudget.yaml | 18 ++ .../templates/service.yaml | 19 ++ .../templates/serviceaccount.yaml | 13 + .../templates/servicemonitor.yaml | 30 ++ .../templates/tests/test-endpoints.yaml | 73 +++++ .../charts/hapi-fhir-jpaserver/values.yaml | 302 ++++++++++++++++++ hapi-fhir-jpaserver/configs/app/index.html | 3 + hapi-fhir-jpaserver/custom/about.html | 14 + hapi-fhir-jpaserver/custom/logo.jpg | Bin 0 -> 48378 bytes hapi-fhir-jpaserver/custom/welcome.html | 14 + 24 files changed, 1208 insertions(+) create mode 100644 hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/Chart.lock create mode 100644 hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/Chart.yaml create mode 100644 hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/README.md create mode 100644 hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/README.md.gotmpl create mode 100644 hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/ci/custom-postgres-user-values.yaml create mode 100644 hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/ci/enabled-ingress-values.yaml create mode 100644 hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/ci/extra-config-values.yaml create mode 100644 hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/ci/extra-volumes-values.yaml create mode 100644 hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/NOTES.txt create mode 100644 hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/_helpers.tpl create mode 100644 hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/application-config.yaml create mode 100644 hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/deployment.yaml create mode 100644 hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/externaldb-secret.yaml create mode 100644 hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/ingress.yaml create mode 100644 hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/poddisruptionbudget.yaml create mode 100644 hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/service.yaml create mode 100644 hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/serviceaccount.yaml create mode 100644 hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/servicemonitor.yaml create mode 100644 hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/tests/test-endpoints.yaml create mode 100644 hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/values.yaml create mode 100644 hapi-fhir-jpaserver/configs/app/index.html create mode 100644 hapi-fhir-jpaserver/custom/about.html create mode 100644 hapi-fhir-jpaserver/custom/logo.jpg create mode 100644 hapi-fhir-jpaserver/custom/welcome.html diff --git a/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/Chart.lock b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/Chart.lock new file mode 100644 index 0000000..8450e96 --- /dev/null +++ b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/Chart.lock @@ -0,0 +1,9 @@ +dependencies: +- name: postgresql + repository: oci://registry-1.docker.io/bitnamicharts + version: 16.5.5 +- name: common + repository: oci://registry-1.docker.io/bitnamicharts + version: 2.30.0 +digest: sha256:c114b296b53007a7973260de8bad61917fe741c0ad9de15fdbeea5743c8f4910 +generated: "2025-03-21T21:44:22.334197366+01:00" diff --git a/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/Chart.yaml b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/Chart.yaml new file mode 100644 index 0000000..43d89c4 --- /dev/null +++ b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/Chart.yaml @@ -0,0 +1,34 @@ +apiVersion: v2 +name: hapi-fhir-jpaserver +description: A Helm chart for deploying the HAPI FHIR JPA server starter on Kubernetes. +type: application +home: https://github.com/hapifhir/hapi-fhir-jpaserver-starter +sources: + - https://github.com/hapifhir/hapi-fhir-jpaserver-starter +dependencies: + - name: postgresql + version: 16.5.5 + repository: oci://registry-1.docker.io/bitnamicharts + condition: postgresql.enabled + - name: common + repository: oci://registry-1.docker.io/bitnamicharts + version: 2.30.0 +appVersion: 8.0.0 +version: 0.19.0 +annotations: + artifacthub.io/license: Apache-2.0 + artifacthub.io/containsSecurityUpdates: "false" + artifacthub.io/operator: "false" + artifacthub.io/prerelease: "false" + artifacthub.io/recommendations: | + - url: https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack + - url: https://artifacthub.io/packages/helm/bitnami/postgresql + artifacthub.io/changes: | + # When using the list of objects option the valid supported kinds are + # added, changed, deprecated, removed, fixed, and security. + - kind: changed + description: "updated postgresql sub-chart to 16.5.5" + - kind: changed + description: "updated curlimages/curl to 8.12.1" + - kind: changed + description: "updated hapiproject/hapi to v8.0.0-1" diff --git a/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/README.md b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/README.md new file mode 100644 index 0000000..45e692b --- /dev/null +++ b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/README.md @@ -0,0 +1,150 @@ +# HAPI FHIR JPA Server Starter Helm Chart + +![Version: 0.19.0](https://img.shields.io/badge/Version-0.19.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 8.0.0](https://img.shields.io/badge/AppVersion-8.0.0-informational?style=flat-square) + +This helm chart will help you install the HAPI FHIR JPA Server in a Kubernetes environment. + +## Sample usage + +```sh +helm repo add hapifhir https://hapifhir.github.io/hapi-fhir-jpaserver-starter/ +helm install hapi-fhir-jpaserver hapifhir/hapi-fhir-jpaserver +``` + +## Requirements + +| Repository | Name | Version | +|------------|------|---------| +| oci://registry-1.docker.io/bitnamicharts | common | 2.30.0 | +| oci://registry-1.docker.io/bitnamicharts | postgresql | 16.5.5 | + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | pod affinity | +| deploymentAnnotations | object | `{}` | annotations applied to the server deployment | +| externalDatabase.database | string | `"fhir"` | database name | +| externalDatabase.existingSecret | string | `""` | name of an existing secret resource containing the DB password in the `existingSecretKey` key | +| externalDatabase.existingSecretKey | string | `"postgresql-password"` | name of the key inside the `existingSecret` | +| externalDatabase.host | string | `"localhost"` | external database host used with `postgresql.enabled=false` | +| externalDatabase.password | string | `""` | database password | +| externalDatabase.port | int | `5432` | database port number | +| externalDatabase.user | string | `"fhir"` | username for the external database | +| extraConfig | string | `""` | additional Spring Boot application config. Mounted as a file and automatically loaded by the application. | +| extraEnv | list | `[]` | extra environment variables to set on the server container | +| extraVolumeMounts | list | `[]` | Optionally specify extra list of additional volumeMounts | +| extraVolumes | list | `[]` | Optionally specify extra list of additional volumes | +| fullnameOverride | string | `""` | override the chart fullname | +| image.pullPolicy | string | `"IfNotPresent"` | image pullPolicy to use | +| image.registry | string | `"docker.io"` | registry where the HAPI FHIR server image is hosted | +| image.repository | string | `"hapiproject/hapi"` | the path inside the repository | +| image.tag | string | `"v8.0.0-1@sha256:9fbac7b012b4be91ba481e7008f1353ede4598bc99a36f3902b8abf873e70ed8"` | the image tag. As of v5.7.0, this is the `distroless` flavor by default, add `-tomcat` to use the Tomcat-based image. | +| imagePullSecrets | list | `[]` | image pull secrets to use when pulling the image | +| ingress.annotations | object | `{}` | provide any additional annotations which may be required. Evaluated as a template. | +| ingress.enabled | bool | `false` | whether to create an Ingress to expose the FHIR server HTTP endpoint | +| ingress.hosts[0].host | string | `"fhir-server.127.0.0.1.nip.io"` | | +| ingress.hosts[0].pathType | string | `"ImplementationSpecific"` | | +| ingress.hosts[0].paths[0] | string | `"/"` | | +| ingress.tls | list | `[]` | ingress TLS config | +| initContainers.resources | object | `{}` | configure the init containers pods resource requests and limits | +| initContainers.resourcesPreset | string | `"nano"` | set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). This is ignored if `resources` is set (`resources` is recommended for production). More information: | +| metrics.service.port | int | `8081` | | +| metrics.serviceMonitor.additionalLabels | object | `{}` | additional labels to apply to the ServiceMonitor object, e.g. `release: prometheus` | +| metrics.serviceMonitor.enabled | bool | `false` | if enabled, creates a ServiceMonitor instance for Prometheus Operator-based monitoring | +| nameOverride | string | `""` | override the chart name | +| nodeSelector | object | `{}` | node selector for the pod | +| podAnnotations | object | `{}` | annotations applied to the server pod | +| podDisruptionBudget.enabled | bool | `false` | Enable PodDisruptionBudget for the server pods. uses policy/v1/PodDisruptionBudget thus requiring k8s 1.21+ | +| podDisruptionBudget.maxUnavailable | string | `""` | maximum unavailable instances | +| podDisruptionBudget.minAvailable | int | `1` | minimum available instances | +| podSecurityContext | object | `{"fsGroup":65532,"fsGroupChangePolicy":"OnRootMismatch","runAsGroup":65532,"runAsNonRoot":true,"runAsUser":65532,"seccompProfile":{"type":"RuntimeDefault"}}` | pod security context | +| postgresql.auth.database | string | `"fhir"` | name for a custom database to create | +| postgresql.auth.existingSecret | string | `""` | Name of existing secret to use for PostgreSQL credentials `auth.postgresPassword`, `auth.password`, and `auth.replicationPassword` will be ignored and picked up from this secret The secret must contain the keys `postgres-password` (which is the password for "postgres" admin user), `password` (which is the password for the custom user to create when `auth.username` is set), and `replication-password` (which is the password for replication user). The secret might also contains the key `ldap-password` if LDAP is enabled. `ldap.bind_password` will be ignored and picked from this secret in this case. The value is evaluated as a template. | +| postgresql.enabled | bool | `true` | enable an included PostgreSQL DB. see for details if set to `false`, the values under `externalDatabase` are used | +| replicaCount | int | `1` | number of replicas to deploy | +| resources | object | `{}` | configure the FHIR server's resource requests and limits | +| resourcesPreset | string | `"medium"` | set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). This is ignored if `resources` is set (`resources` is recommended for production). More information: | +| securityContext.allowPrivilegeEscalation | bool | `false` | | +| securityContext.capabilities.drop[0] | string | `"ALL"` | | +| securityContext.privileged | bool | `false` | | +| securityContext.readOnlyRootFilesystem | bool | `true` | | +| securityContext.runAsGroup | int | `65532` | | +| securityContext.runAsNonRoot | bool | `true` | | +| securityContext.runAsUser | int | `65532` | | +| securityContext.seccompProfile.type | string | `"RuntimeDefault"` | | +| service.port | int | `8080` | port where the server will be exposed at | +| service.type | string | `"ClusterIP"` | service type | +| 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 | +| tests.automountServiceAccountToken | bool | `false` | whether the service account token should be auto-mounted for the test pods | +| tests.resources | object | `{}` | configure the test pods resource requests and limits | +| tests.resourcesPreset | string | `"nano"` | set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). This is ignored if `resources` is set (`resources` is recommended for production). More information: | +| tolerations | list | `[]` | pod tolerations | +| topologySpreadConstraints | list | `[]` | pod topology spread configuration see: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/#api | + +## Development + +To update the Helm chart when a new version of the `hapiproject/hapi` image is released, [values.yaml](values.yaml) `image.tag` and the [Chart.yaml](Chart.yaml)'s +`version` and optionally the `appVersion` field need to be updated. Afterwards, re-generate the [README.md](README.md) +by running: + +```sh +$ helm-docs +INFO[2021-11-20T12:38:04Z] Found Chart directories [charts/hapi-fhir-jpaserver] +INFO[2021-11-20T12:38:04Z] Generating README Documentation for chart /usr/src/app/charts/hapi-fhir-jpaserver +``` + +## Enable Distributed Tracing based on the OpenTelemtry Java Agent + +The container image includes the [OpenTelemetry Java agent JAR](https://github.com/open-telemetry/opentelemetry-java-instrumentation) +which can be used to enable distributed tracing. It can be configured entirely using environment variables, +see for details. + +Here's an example setup deploying [Jaeger](https://www.jaegertracing.io/) as a tracing backend: + +```sh +# required by the Jaeger Operator +kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.yaml +kubectl create namespace observability +kubectl create -f https://github.com/jaegertracing/jaeger-operator/releases/download/v1.37.0/jaeger-operator.yaml -n observability + +cat < in your browser. + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/README.md.gotmpl b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/README.md.gotmpl new file mode 100644 index 0000000..5588a2c --- /dev/null +++ b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/README.md.gotmpl @@ -0,0 +1,79 @@ +# HAPI FHIR JPA Server Starter Helm Chart + +{{ template "chart.versionBadge" . }}{{ template "chart.typeBadge" . }}{{ template "chart.appVersionBadge" . }} + +This helm chart will help you install the HAPI FHIR JPA Server in a Kubernetes environment. + +## Sample usage + +```sh +helm repo add hapifhir https://hapifhir.github.io/hapi-fhir-jpaserver-starter/ +helm install hapi-fhir-jpaserver hapifhir/hapi-fhir-jpaserver +``` + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +## Development + +To update the Helm chart when a new version of the `hapiproject/hapi` image is released, [values.yaml](values.yaml) `image.tag` and the [Chart.yaml](Chart.yaml)'s +`version` and optionally the `appVersion` field need to be updated. Afterwards, re-generate the [README.md](README.md) +by running: + +```sh +$ helm-docs +INFO[2021-11-20T12:38:04Z] Found Chart directories [charts/hapi-fhir-jpaserver] +INFO[2021-11-20T12:38:04Z] Generating README Documentation for chart /usr/src/app/charts/hapi-fhir-jpaserver +``` + +## Enable Distributed Tracing based on the OpenTelemtry Java Agent + +The container image includes the [OpenTelemetry Java agent JAR](https://github.com/open-telemetry/opentelemetry-java-instrumentation) +which can be used to enable distributed tracing. It can be configured entirely using environment variables, +see for details. + +Here's an example setup deploying [Jaeger](https://www.jaegertracing.io/) as a tracing backend: + +```sh +# required by the Jaeger Operator +kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.yaml +kubectl create namespace observability +kubectl create -f https://github.com/jaegertracing/jaeger-operator/releases/download/v1.37.0/jaeger-operator.yaml -n observability + +cat < in your browser. + +{{ template "helm-docs.versionFooter" . }} diff --git a/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/ci/custom-postgres-user-values.yaml b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/ci/custom-postgres-user-values.yaml new file mode 100644 index 0000000..895f891 --- /dev/null +++ b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/ci/custom-postgres-user-values.yaml @@ -0,0 +1,7 @@ +postgresql: + enabled: true + auth: + username: hapi_fhir_jpaserver_starter_user + database: hapi_fhir_jpaserver_starter + password: secret_user_password + postgresPassword: secret_postgres_password diff --git a/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/ci/enabled-ingress-values.yaml b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/ci/enabled-ingress-values.yaml new file mode 100644 index 0000000..e1b053b --- /dev/null +++ b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/ci/enabled-ingress-values.yaml @@ -0,0 +1,6 @@ +ingress: + enabled: true + +postgresql: + auth: + postgresPassword: secretpassword diff --git a/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/ci/extra-config-values.yaml b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/ci/extra-config-values.yaml new file mode 100644 index 0000000..a602b3c --- /dev/null +++ b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/ci/extra-config-values.yaml @@ -0,0 +1,17 @@ +extraConfig: | + hapi: + fhir: + cr_enabled: true + tester: + home: + name: Hello HAPI FHIR + server_address: "http://fhir-server.127.0.0.1.nip.io/fhir" + refuse_to_fetch_third_party_urls: true + fhir_version: R4 + +ingress: + enabled: true + hosts: + - host: fhir-server.127.0.0.1.nip.io + pathType: ImplementationSpecific + paths: ["/"] diff --git a/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/ci/extra-volumes-values.yaml b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/ci/extra-volumes-values.yaml new file mode 100644 index 0000000..748f4e6 --- /dev/null +++ b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/ci/extra-volumes-values.yaml @@ -0,0 +1,11 @@ +extraVolumes: + - name: config-kube-root-ca + configMap: + name: kube-root-ca.crt + items: + - key: ca.crt + path: ca.crt + +extraVolumeMounts: + - name: config-kube-root-ca + mountPath: /etc/test diff --git a/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/NOTES.txt b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/NOTES.txt new file mode 100644 index 0000000..2375278 --- /dev/null +++ b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "hapi-fhir-jpaserver.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "hapi-fhir-jpaserver.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "hapi-fhir-jpaserver.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "hapi-fhir-jpaserver.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/_helpers.tpl b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/_helpers.tpl new file mode 100644 index 0000000..f8ce389 --- /dev/null +++ b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/_helpers.tpl @@ -0,0 +1,152 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "hapi-fhir-jpaserver.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "hapi-fhir-jpaserver.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "hapi-fhir-jpaserver.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "hapi-fhir-jpaserver.labels" -}} +helm.sh/chart: {{ include "hapi-fhir-jpaserver.chart" . }} +{{ include "hapi-fhir-jpaserver.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "hapi-fhir-jpaserver.selectorLabels" -}} +app.kubernetes.io/name: {{ include "hapi-fhir-jpaserver.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "hapi-fhir-jpaserver.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "hapi-fhir-jpaserver.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Create a default fully qualified postgresql name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "hapi-fhir-jpaserver.postgresql.fullname" -}} +{{- $name := default "postgresql" .Values.postgresql.nameOverride -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Get the Postgresql credentials secret name. +*/}} +{{- define "hapi-fhir-jpaserver.postgresql.secretName" -}} +{{- if .Values.postgresql.enabled -}} + {{- if .Values.postgresql.auth.existingSecret -}} + {{- printf "%s" .Values.postgresql.auth.existingSecret -}} + {{- else -}} + {{- printf "%s" (include "hapi-fhir-jpaserver.postgresql.fullname" .) -}} + {{- end -}} +{{- else }} + {{- if .Values.externalDatabase.existingSecret -}} + {{- printf "%s" .Values.externalDatabase.existingSecret -}} + {{- else -}} + {{ printf "%s-%s" (include "hapi-fhir-jpaserver.fullname" .) "external-db" }} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Get the Postgresql credentials secret key. +*/}} +{{- define "hapi-fhir-jpaserver.postgresql.secretKey" -}} +{{- if .Values.postgresql.enabled -}} + {{- if .Values.postgresql.auth.username -}} + {{- printf "%s" .Values.postgresql.auth.secretKeys.userPasswordKey -}} + {{- else -}} + {{- printf "%s" .Values.postgresql.auth.secretKeys.adminPasswordKey -}} + {{- end -}} +{{- else }} + {{- if .Values.externalDatabase.existingSecret -}} + {{- printf "%s" .Values.externalDatabase.existingSecretKey -}} + {{- else -}} + {{- printf "postgres-password" -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Add environment variables to configure database values +*/}} +{{- define "hapi-fhir-jpaserver.database.host" -}} +{{- ternary (include "hapi-fhir-jpaserver.postgresql.fullname" .) .Values.externalDatabase.host .Values.postgresql.enabled -}} +{{- end -}} + +{{/* +Add environment variables to configure database values +*/}} +{{- define "hapi-fhir-jpaserver.database.user" -}} +{{- if .Values.postgresql.enabled -}} + {{- printf "%s" .Values.postgresql.auth.username | default "postgres" -}} +{{- else -}} + {{- printf "%s" .Values.externalDatabase.user -}} +{{- end -}} +{{- end -}} + +{{/* +Add environment variables to configure database values +*/}} +{{- define "hapi-fhir-jpaserver.database.name" -}} +{{- ternary .Values.postgresql.auth.database .Values.externalDatabase.database .Values.postgresql.enabled -}} +{{- end -}} + +{{/* +Add environment variables to configure database values +*/}} +{{- define "hapi-fhir-jpaserver.database.port" -}} +{{- ternary "5432" .Values.externalDatabase.port .Values.postgresql.enabled -}} +{{- end -}} + +{{/* +Create the JDBC URL from the host, port and database name. +*/}} +{{- define "hapi-fhir-jpaserver.database.jdbcUrl" -}} +{{- $host := (include "hapi-fhir-jpaserver.database.host" .) -}} +{{- $port := (include "hapi-fhir-jpaserver.database.port" .) -}} +{{- $name := (include "hapi-fhir-jpaserver.database.name" .) -}} +{{- $appName := .Release.Name -}} +{{ printf "jdbc:postgresql://%s:%d/%s?ApplicationName=%s" $host (int $port) $name $appName }} +{{- end -}} diff --git a/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/application-config.yaml b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/application-config.yaml new file mode 100644 index 0000000..59bae19 --- /dev/null +++ b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/application-config.yaml @@ -0,0 +1,11 @@ +{{- if .Values.extraConfig -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "hapi-fhir-jpaserver.fullname" . }}-application-config + labels: + {{- include "hapi-fhir-jpaserver.labels" . | nindent 4 }} +data: + application-extra.yaml: |- + {{ .Values.extraConfig | nindent 4 }} +{{- end }} diff --git a/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/deployment.yaml b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/deployment.yaml new file mode 100644 index 0000000..7adfec7 --- /dev/null +++ b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/deployment.yaml @@ -0,0 +1,160 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "hapi-fhir-jpaserver.fullname" . }} + labels: + {{- include "hapi-fhir-jpaserver.labels" . | nindent 4 }} + {{- with .Values.deploymentAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "hapi-fhir-jpaserver.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "hapi-fhir-jpaserver.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "hapi-fhir-jpaserver.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + initContainers: + - name: wait-for-db-to-be-ready + image: docker.io/bitnami/postgresql:17.4.0-debian-12-r10@sha256:7b9af9dd759055265998bbf12368e6d7d6326e6fd23f8157be841fad0915c1a1 + imagePullPolicy: IfNotPresent + {{- with .Values.restrictedContainerSecurityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if .Values.initContainers.resources }} + resources: {{- toYaml .Values.initContainers.resources | nindent 12 }} + {{- else if ne .Values.initContainers.resourcesPreset "none" }} + resources: {{- include "common.resources.preset" (dict "type" .Values.initContainers.resourcesPreset) | nindent 12 }} + {{- end }} + env: + - name: PGHOST + value: "{{ include "hapi-fhir-jpaserver.database.host" . }}" + - name: PGPORT + value: "{{ include "hapi-fhir-jpaserver.database.port" . }}" + - name: PGUSER + value: "{{ include "hapi-fhir-jpaserver.database.user" . }}" + command: ["/bin/sh", "-c"] + args: + - | + until pg_isready; do + echo "Waiting for DB ${PGUSER}@${PGHOST}:${PGPORT} to be up"; + sleep 15; + done; + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: {{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: 8080 + protocol: TCP + - name: http-metrics + containerPort: 8081 + protocol: TCP + {{- with .Values.startupProbe }} + startupProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if .Values.resources }} + resources: {{- toYaml .Values.resources | nindent 12 }} + {{- else if ne .Values.resourcesPreset "none" }} + resources: {{- include "common.resources.preset" (dict "type" .Values.resourcesPreset) | nindent 12 }} + {{- end }} + env: + - name: SPRING_DATASOURCE_URL + value: {{ include "hapi-fhir-jpaserver.database.jdbcUrl" $ }} + - name: SPRING_DATASOURCE_USERNAME + value: {{ include "hapi-fhir-jpaserver.database.user" $ }} + - name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "hapi-fhir-jpaserver.postgresql.secretName" . }} + key: {{ include "hapi-fhir-jpaserver.postgresql.secretKey" . }} + - name: SPRING_DATASOURCE_DRIVERCLASSNAME + value: org.postgresql.Driver + - name: spring.jpa.properties.hibernate.dialect + value: ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect + - name: HAPI_FHIR_USE_APACHE_ADDRESS_STRATEGY + value: "true" + - name: MANAGEMENT_ENDPOINT_HEALTH_PROBES_ADD_ADDITIONAL_PATHS + value: "true" + - name: MANAGEMENT_SERVER_PORT + value: "8081" + - name: MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE + value: "health,prometheus" + {{- if .Values.extraConfig }} + - name: SPRING_CONFIG_IMPORT + value: "/app/config/application-extra.yaml" + {{- end }} + {{- if .Values.extraEnv }} + {{ toYaml .Values.extraEnv | nindent 12 }} + {{- end }} + volumeMounts: + - mountPath: /tmp + name: tmp-volume + - mountPath: /app/target + name: lucenefiles-volume + {{- if .Values.extraConfig }} + - name: application-extra-config + mountPath: /app/config/application-extra.yaml + readOnly: true + subPath: application-extra.yaml + {{- end }} + {{- if .Values.extraVolumeMounts }} + {{- include "common.tplvalues.render" (dict "value" .Values.extraVolumeMounts "context" $) | nindent 12 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: tmp-volume + emptyDir: {} + - name: lucenefiles-volume + emptyDir: {} + {{- if .Values.extraConfig }} + - name: application-extra-config + configMap: + name: {{ include "hapi-fhir-jpaserver.fullname" . }}-application-config + {{- end }} + {{- if .Values.extraVolumes }} + {{- include "common.tplvalues.render" (dict "value" .Values.extraVolumes "context" $) | nindent 8 }} + {{- end }} diff --git a/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/externaldb-secret.yaml b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/externaldb-secret.yaml new file mode 100644 index 0000000..f6411e6 --- /dev/null +++ b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/externaldb-secret.yaml @@ -0,0 +1,11 @@ +{{- if and (not .Values.postgresql.enabled) (not .Values.externalDatabase.existingSecret) (not .Values.postgresql.auth.existingSecret) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "hapi-fhir-jpaserver.fullname" . }}-external-db + labels: + {{- include "hapi-fhir-jpaserver.labels" . | nindent 4 }} +type: Opaque +data: + postgres-password: {{ .Values.externalDatabase.password | b64enc | quote }} +{{- end }} diff --git a/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/ingress.yaml b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/ingress.yaml new file mode 100644 index 0000000..7206a88 --- /dev/null +++ b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/ingress.yaml @@ -0,0 +1,53 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "hapi-fhir-jpaserver.fullname" . -}} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion }} +apiVersion: networking.k8s.io/v1beta1 +{{ else }} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "hapi-fhir-jpaserver.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: +{{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} + rules: + {{- range .Values.ingress.hosts }} + {{- $pathType := .pathType }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ . }} + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + pathType: {{ $pathType | default "ImplementationSpecific" }} + {{- 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/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/poddisruptionbudget.yaml b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/poddisruptionbudget.yaml new file mode 100644 index 0000000..1b1b533 --- /dev/null +++ b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/poddisruptionbudget.yaml @@ -0,0 +1,18 @@ +{{- if .Values.podDisruptionBudget.enabled }} +kind: PodDisruptionBudget +apiVersion: policy/v1 +metadata: + name: {{ include "hapi-fhir-jpaserver.fullname" . }} + labels: + {{- include "hapi-fhir-jpaserver.labels" . | nindent 4 }} +spec: + {{- if .Values.podDisruptionBudget.minAvailable }} + minAvailable: {{ .Values.podDisruptionBudget.minAvailable }} + {{- end }} + {{- if .Values.podDisruptionBudget.maxUnavailable }} + maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }} + {{- end }} + selector: + matchLabels: + {{- include "hapi-fhir-jpaserver.selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/service.yaml b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/service.yaml new file mode 100644 index 0000000..bf4723c --- /dev/null +++ b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "hapi-fhir-jpaserver.fullname" . }} + labels: + {{- include "hapi-fhir-jpaserver.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + - port: {{ .Values.metrics.service.port }} + targetPort: http-metrics + protocol: TCP + name: http-metrics + selector: + {{- include "hapi-fhir-jpaserver.selectorLabels" . | nindent 4 }} diff --git a/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/serviceaccount.yaml b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/serviceaccount.yaml new file mode 100644 index 0000000..3ce5e59 --- /dev/null +++ b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "hapi-fhir-jpaserver.serviceAccountName" . }} + labels: + {{- include "hapi-fhir-jpaserver.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/servicemonitor.yaml b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/servicemonitor.yaml new file mode 100644 index 0000000..d945cd6 --- /dev/null +++ b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/servicemonitor.yaml @@ -0,0 +1,30 @@ +{{- if .Values.metrics.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "hapi-fhir-jpaserver.fullname" . }} + {{- if .Values.metrics.serviceMonitor.namespace }} + namespace: {{ .Values.metrics.serviceMonitor.namespace }} + {{- end }} + labels: + {{- include "hapi-fhir-jpaserver.labels" . | nindent 4 }} + {{- if .Values.metrics.serviceMonitor.additionalLabels }} + {{- toYaml .Values.metrics.serviceMonitor.additionalLabels | nindent 4 }} + {{- end }} +spec: + endpoints: + - port: http-metrics + path: /actuator/prometheus + {{- if .Values.metrics.serviceMonitor.interval }} + interval: {{ .Values.metrics.serviceMonitor.interval }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ .Values.metrics.serviceMonitor.scrapeTimeout }} + {{- end }} + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} + selector: + matchLabels: + {{- include "hapi-fhir-jpaserver.selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/tests/test-endpoints.yaml b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/tests/test-endpoints.yaml new file mode 100644 index 0000000..703a0e0 --- /dev/null +++ b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/templates/tests/test-endpoints.yaml @@ -0,0 +1,73 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "hapi-fhir-jpaserver.fullname" . }}-test-endpoints" + labels: + {{- include "hapi-fhir-jpaserver.labels" . | nindent 4 }} + {{ include "hapi-fhir-jpaserver.fullname" . }}-client: "true" + app.kubernetes.io/component: tests + annotations: + "helm.sh/hook": test +spec: + restartPolicy: Never + automountServiceAccountToken: {{ .Values.tests.automountServiceAccountToken }} + securityContext: + {{- toYaml .Values.tests.podSecurityContext | nindent 4 }} + containers: + - name: test-metadata-endpoint + image: "{{ .Values.curl.image.registry }}/{{ .Values.curl.image.repository }}:{{ .Values.curl.image.tag }}" + command: ["curl", "--fail-with-body"] + args: ["http://{{ include "hapi-fhir-jpaserver.fullname" . }}:{{ .Values.service.port }}/fhir/metadata?_summary=true"] + {{- with .Values.restrictedContainerSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.tests.resources }} + resources: {{- toYaml .Values.tests.resources | nindent 10 }} + {{- else if ne .Values.tests.resourcesPreset "none" }} + resources: {{- include "common.resources.preset" (dict "type" .Values.tests.resourcesPreset) | nindent 10 }} + {{- end }} + livenessProbe: + exec: + command: ["true"] + readinessProbe: + exec: + command: ["true"] + - name: test-patient-endpoint + image: "{{ .Values.curl.image.registry }}/{{ .Values.curl.image.repository }}:{{ .Values.curl.image.tag }}" + command: ["curl", "--fail-with-body"] + args: ["http://{{ include "hapi-fhir-jpaserver.fullname" . }}:{{ .Values.service.port }}/fhir/Patient?_count=1&_summary=true"] + {{- with .Values.restrictedContainerSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.tests.resources }} + resources: {{- toYaml .Values.tests.resources | nindent 10 }} + {{- else if ne .Values.tests.resourcesPreset "none" }} + resources: {{- include "common.resources.preset" (dict "type" .Values.tests.resourcesPreset) | nindent 10 }} + {{- end }} + livenessProbe: + exec: + command: ["true"] + readinessProbe: + exec: + command: ["true"] + - name: test-metrics-endpoint + image: "{{ .Values.curl.image.registry }}/{{ .Values.curl.image.repository }}:{{ .Values.curl.image.tag }}" + command: ["curl", "--fail-with-body"] + args: ["http://{{ include "hapi-fhir-jpaserver.fullname" . }}:{{ .Values.metrics.service.port }}/actuator/prometheus"] + {{- with .Values.restrictedContainerSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.tests.resources }} + resources: {{- toYaml .Values.tests.resources | nindent 10 }} + {{- else if ne .Values.tests.resourcesPreset "none" }} + resources: {{- include "common.resources.preset" (dict "type" .Values.tests.resourcesPreset) | nindent 10 }} + {{- end }} + livenessProbe: + exec: + command: ["true"] + readinessProbe: + exec: + command: ["true"] diff --git a/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/values.yaml b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/values.yaml new file mode 100644 index 0000000..aac23b0 --- /dev/null +++ b/hapi-fhir-jpaserver/charts/hapi-fhir-jpaserver/values.yaml @@ -0,0 +1,302 @@ +# -- number of replicas to deploy +replicaCount: 1 + +image: + # -- registry where the HAPI FHIR server image is hosted + registry: docker.io + # -- the path inside the repository + repository: hapiproject/hapi + # -- the image tag. As of v5.7.0, this is the `distroless` flavor by default, add `-tomcat` to use the Tomcat-based image. + tag: "v8.0.0-1@sha256:9fbac7b012b4be91ba481e7008f1353ede4598bc99a36f3902b8abf873e70ed8" + # -- image pullPolicy to use + pullPolicy: IfNotPresent + +# -- image pull secrets to use when pulling the image +imagePullSecrets: [] + +# -- override the chart name +nameOverride: "" + +# -- override the chart fullname +fullnameOverride: "" + +# -- annotations applied to the server deployment +deploymentAnnotations: {} + +# -- annotations applied to the server pod +podAnnotations: {} + +# -- pod security context +podSecurityContext: + fsGroupChangePolicy: OnRootMismatch + runAsNonRoot: true + runAsGroup: 65532 + runAsUser: 65532 + fsGroup: 65532 + seccompProfile: + type: RuntimeDefault + +securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 65532 + runAsGroup: 65532 + privileged: false + seccompProfile: + type: RuntimeDefault + +# service to expose the server +service: + # -- service type + type: ClusterIP + # -- port where the server will be exposed at + port: 8080 + +ingress: + # -- whether to create an Ingress to expose the FHIR server HTTP endpoint + enabled: false + # -- provide any additional annotations which may be required. Evaluated as a template. + annotations: + {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: fhir-server.127.0.0.1.nip.io + pathType: ImplementationSpecific + paths: ["/"] + # -- ingress TLS config + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +# -- set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). +# This is ignored if `resources` is set (`resources` is recommended for production). +# More information: +resourcesPreset: "medium" + +# -- configure the FHIR server's resource requests and limits +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 + # requests: + # cpu: 100m + # memory: 128Mi + +# -- node selector for the pod +nodeSelector: {} + +# -- pod tolerations +tolerations: [] + +# -- pod affinity +affinity: {} + +# -- pod topology spread configuration +# see: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/#api +topologySpreadConstraints: + [] + # - maxSkew: 1 + # topologyKey: topology.kubernetes.io/zone + # whenUnsatisfiable: ScheduleAnyway + # labelSelector: + # matchLabels: + # app.kubernetes.io/instance: hapi-fhir-jpaserver + # app.kubernetes.io/name: hapi-fhir-jpaserver + +postgresql: + # -- enable an included PostgreSQL DB. + # see for details + # if set to `false`, the values under `externalDatabase` are used + enabled: true + auth: + # -- name for a custom database to create + database: "fhir" + # -- Name of existing secret to use for PostgreSQL credentials + # `auth.postgresPassword`, `auth.password`, and `auth.replicationPassword` will be ignored and picked up from this secret + # The secret must contain the keys `postgres-password` (which is the password for "postgres" admin user), + # `password` (which is the password for the custom user to create when `auth.username` is set), + # and `replication-password` (which is the password for replication user). + # The secret might also contains the key `ldap-password` if LDAP is enabled. `ldap.bind_password` will be ignored and + # picked from this secret in this case. + # The value is evaluated as a template. + existingSecret: "" + +# -- readiness probe +# @ignored +readinessProbe: + httpGet: + path: /readyz + port: http + failureThreshold: 5 + initialDelaySeconds: 30 + periodSeconds: 20 + successThreshold: 1 + timeoutSeconds: 20 + +# -- liveness probe +# @ignored +livenessProbe: + httpGet: + path: /livez + port: http + failureThreshold: 5 + initialDelaySeconds: 30 + periodSeconds: 20 + successThreshold: 1 + timeoutSeconds: 30 + +# -- startup probe +# @ignored +startupProbe: + httpGet: + path: /readyz + port: http + failureThreshold: 10 + initialDelaySeconds: 30 + periodSeconds: 30 + successThreshold: 1 + timeoutSeconds: 30 + +externalDatabase: + # -- external database host used with `postgresql.enabled=false` + host: localhost + # -- database port number + port: 5432 + # -- username for the external database + user: fhir + # -- database password + password: "" + # -- name of an existing secret resource containing the DB password in the `existingSecretKey` key + existingSecret: "" + # -- name of the key inside the `existingSecret` + existingSecretKey: "postgresql-password" + # -- database name + database: fhir + +# -- extra environment variables to set on the server container +extraEnv: + [] + # - name: SPRING_FLYWAY_BASELINE_ON_MIGRATE + # value: "true" + +podDisruptionBudget: + # -- Enable PodDisruptionBudget for the server pods. + # uses policy/v1/PodDisruptionBudget thus requiring k8s 1.21+ + enabled: false + # -- minimum available instances + minAvailable: 1 + # -- maximum unavailable instances + maxUnavailable: "" + +serviceAccount: + # -- Specifies whether a service account should be created. + create: false + # -- Annotations to add to the service account + annotations: {} + # -- The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + # -- Automatically mount a ServiceAccount's API credentials? + automount: true + +metrics: + serviceMonitor: + # -- if enabled, creates a ServiceMonitor instance for Prometheus Operator-based monitoring + enabled: false + # -- additional labels to apply to the ServiceMonitor object, e.g. `release: prometheus` + additionalLabels: {} + # namespace: monitoring + # interval: 30s + # scrapeTimeout: 10s + service: + port: 8081 + +# @ignore +restrictedContainerSecurityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + privileged: false + capabilities: + drop: + - ALL + runAsNonRoot: true + runAsUser: 65534 + runAsGroup: 65534 + seccompProfile: + type: RuntimeDefault + +# @ignored +curl: + image: + registry: docker.io + repository: curlimages/curl + tag: 8.12.1@sha256:94e9e444bcba979c2ea12e27ae39bee4cd10bc7041a472c4727a558e213744e6 + +tests: + # -- whether the service account token should be auto-mounted for the test pods + automountServiceAccountToken: false + # -- set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). + # This is ignored if `resources` is set (`resources` is recommended for production). + # More information: + resourcesPreset: "nano" + # -- configure the test pods resource requests and limits + resources: {} + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + # @ignored + podSecurityContext: + fsGroupChangePolicy: OnRootMismatch + runAsNonRoot: true + runAsGroup: 65532 + runAsUser: 65532 + fsGroup: 65532 + seccompProfile: + type: RuntimeDefault + +initContainers: + # -- set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). + # This is ignored if `resources` is set (`resources` is recommended for production). + # More information: + resourcesPreset: "nano" + # -- configure the init containers pods resource requests and limits + resources: {} + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +# -- additional Spring Boot application config. Mounted as a file and automatically loaded by the application. +extraConfig: + "" + # # For example: + # | + # hapi: + # fhir: + # implementationguides: + # gh_0_1_0: + # url: https://build.fhir.org/ig/hl7-eu/gravitate-health/package.tgz + # name: hl7.eu.fhir.gh + # version: 0.1.0 + +# -- Optionally specify extra list of additional volumes +extraVolumes: [] + +# -- Optionally specify extra list of additional volumeMounts +extraVolumeMounts: [] diff --git a/hapi-fhir-jpaserver/configs/app/index.html b/hapi-fhir-jpaserver/configs/app/index.html new file mode 100644 index 0000000..638e346 --- /dev/null +++ b/hapi-fhir-jpaserver/configs/app/index.html @@ -0,0 +1,3 @@ +

+ Greetings from the custom web app page! +

\ No newline at end of file diff --git a/hapi-fhir-jpaserver/custom/about.html b/hapi-fhir-jpaserver/custom/about.html new file mode 100644 index 0000000..6409c77 --- /dev/null +++ b/hapi-fhir-jpaserver/custom/about.html @@ -0,0 +1,14 @@ +

+ This is a custom about page! It means you have configured 'custom_content_path: ./custom' in the application.yaml +

+

+ This server provides a complete implementation of the FHIR Specification + using a 100% open source software stack. +

+

+ This server is built + from a number of modules of the + HAPI FHIR + project, which is a 100% open-source (Apache 2.0 Licensed) Java based + implementation of the FHIR specification. +

\ No newline at end of file diff --git a/hapi-fhir-jpaserver/custom/logo.jpg b/hapi-fhir-jpaserver/custom/logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3a5aad0b6d2ce87c6a585c4a3057c92d1e6881d7 GIT binary patch literal 48378 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 M(>eXex%@rxZ}K4ha{vGU literal 0 HcmV?d00001 diff --git a/hapi-fhir-jpaserver/custom/welcome.html b/hapi-fhir-jpaserver/custom/welcome.html new file mode 100644 index 0000000..36d2c08 --- /dev/null +++ b/hapi-fhir-jpaserver/custom/welcome.html @@ -0,0 +1,14 @@ +

+ This is a custom welcome page! It means you have configured 'custom_content_path: ./custom' in the application.yaml +

+

+ This server provides a complete implementation of the FHIR Specification + using a 100% open source software stack. +

+

+ This server is built + from a number of modules of the + HAPI FHIR + project, which is a 100% open-source (Apache 2.0 Licensed) Java based + implementation of the FHIR specification. +

\ No newline at end of file