From 25e997770f00079703f7b0767dd08716aa756efb Mon Sep 17 00:00:00 2001 From: cmotadev Date: Sat, 20 Sep 2025 21:12:16 -0300 Subject: [PATCH] First version of helm chart of pygeoapi --- .gitignore | 4 + helm/.helmignore | 23 +++ helm/Chart.yaml | 23 +++ helm/README.md | 206 ++++++++++++++++++++++ helm/deploys/values.crc.yaml | 31 ++++ helm/deploys/values.minikube.yaml | 43 +++++ helm/templates/NOTES.txt | 22 +++ helm/templates/_helpers.tpl | 73 ++++++++ helm/templates/configmap.yaml | 18 ++ helm/templates/deployment.yaml | 104 +++++++++++ helm/templates/hpa.yaml | 32 ++++ helm/templates/ingress.yaml | 61 +++++++ helm/templates/secret.yaml | 0 helm/templates/service.yaml | 15 ++ helm/templates/serviceaccount.yaml | 13 ++ helm/templates/tests/test-connection.yaml | 15 ++ helm/values.yaml | 113 ++++++++++++ 17 files changed, 796 insertions(+) create mode 100644 helm/.helmignore create mode 100644 helm/Chart.yaml create mode 100644 helm/README.md create mode 100644 helm/deploys/values.crc.yaml create mode 100644 helm/deploys/values.minikube.yaml create mode 100644 helm/templates/NOTES.txt create mode 100644 helm/templates/_helpers.tpl create mode 100644 helm/templates/configmap.yaml create mode 100644 helm/templates/deployment.yaml create mode 100644 helm/templates/hpa.yaml create mode 100644 helm/templates/ingress.yaml create mode 100644 helm/templates/secret.yaml create mode 100644 helm/templates/service.yaml create mode 100644 helm/templates/serviceaccount.yaml create mode 100644 helm/templates/tests/test-connection.yaml create mode 100644 helm/values.yaml diff --git a/.gitignore b/.gitignore index 68bc17f..938e6aa 100644 --- a/.gitignore +++ b/.gitignore @@ -158,3 +158,7 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ + +# Helm +helm/*.tgz +helm/Chart.lock diff --git a/helm/.helmignore b/helm/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/helm/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/helm/Chart.yaml b/helm/Chart.yaml new file mode 100644 index 0000000..e8daf45 --- /dev/null +++ b/helm/Chart.yaml @@ -0,0 +1,23 @@ +apiVersion: v2 +name: pygeoapi +version: 0.1.0 +description: |- + pygeoapi is a Python server implementation of the OGC API suite of standards. + The project emerged as part of the next generation OGC API efforts in 2018 + and provides the capability for organizations to deploy a RESTful OGC API + endpoint using OpenAPI, GeoJSON, and HTML. +type: application +keywords: + - GIS + - FOSS4G + - OGC API + - python +home: https://pygeoapi.io/ +sources: + - https://github.com/geopython/pygeoapi + - https://github.com/geopython/pygeoapi-examples +maintainers: + - name: Carlos Eduardo Mota + email: cmota.dev@gmail.com +icon: "https://pygeoapi.io/img/pygeoapi-logo.png" +appVersion: "0.21.0" diff --git a/helm/README.md b/helm/README.md new file mode 100644 index 0000000..39d66e9 --- /dev/null +++ b/helm/README.md @@ -0,0 +1,206 @@ +# pygeoapi Helm Chart + +This Helm chart deploys a **pygeoapi** instance, a server that publishes geospatial data using Open Geospatial Consortium (OGC) standards and the OGC API Features. This chart is designed to be flexible and easy to configure, allowing you to customize the deployment to fit your needs. 🚀 + + +## **Local Installation** + +To install this chart locally, you'll need a Kubernetes cluster running on your machine, such as **kind**, **minikube**, **CRC/OpenShift Local**, or **Docker Desktop** with Kubernetes enabled. Once your local cluster is up and running, you can install the chart directly from your local file system. + +First, navigate to the chart's directory: + +```bash +cd path/to/your/pygeoapi-helm-chart +``` + +### Default deploy install +Next, use the `helm install` command, specifying the release name and the local chart path (`.`) : + +```bash +helm install [YOUR_RELEASE_NAME] . +``` + +### Install with custom values.yaml +To customize the installation, you can create a custom `values.yaml` file and pass it during installation: + +```yaml +# custom values.yaml example +ingress: + enabled: true + hosts: + - host: "ogcapi-pygeoapi.minikube.local" + paths: + - path: / + pathType: ImplementationSpecific + +service: + type: NodePort # minikube single node + +settings: + config: + enabled: true + name: pygeoapi-custom-config + isEncrypted: true # false -> configMap, true -> secret + serverAdmin: false + hotReload: True + wsgi: + workers: 5 + workerTimeout: 6000 + workerClass: gevent +``` +To modify pygeoapi's behavior, check the "pygeoapi settings"session below. + +Now, to deploy pygeoapi with custom values.yaml, just run the lines below + +```bash +# first create a configmap with pygeoapi config file +# if config.enabled=true and config.isEncrypted=false +kubectl create configmap pygeoapi-custom-config --from-file=local.config.yml=path/to/custom/pygeoapi-config.yml + +# ... or create a secret, if you have sensitive information +# (config.enabled=true and config.isEncrypted=true) +kubectl create secret generic pygeoapi-custom-config --from-file=local.config.yml=path/to/custom/pygeoapi-config.yml + +# then install +helm install [YOUR_RELEASE_NAME] . -f my-custom-values.yaml +``` + +### Openshift-specific setiings + +In OpenShift some additional config needs to be set, because pygeoapi still has issues running as non-root user. Three additional steps needs to be set before helm install + +1. Add serviceAccount +```yaml +# custom values.yaml example +# We have to define a ServiceAccount for deployment +... +serviceAccount: + create: true + name: pygeoapi +... +``` + +2. Create a RoleBinding allowing the service account (-z parameter) to run pods as AnyUID, on pygeoapi's project (-n parameter). See below + +```bash +# run as kubeadmin or any user capable +oc adm policy add-scc-to-user anyuid -z pygeoapi -n teste +``` + +3. Create configmap or secret, if necessary, like kubectl but changing it to oc. + +```bash +# first create a configmap with pygeoapi config file +# if config.enabled=true and config.isEncrypted=false +oc create configmap pygeoapi-custom-config --from-file=local.config.yml=path/to/custom/pygeoapi-config.yml + +# ... or create a secret, if you have sensitive information +# (config.enabled=true and config.isEncrypted=true) +oc create secret generic pygeoapi-custom-config --from-file=local.config.yml=path/to/custom/pygeoapi-config.yml + +# then install +helm install [YOUR_RELEASE_NAME] . -f my-custom-values.yaml +``` + +* In *deploys* directory, **there's an example of a customized values for minikube and CRC/OpenShift Local**. + +This will deploy the pygeoapi application to your local cluster, allowing you to test configurations and verify functionality before pushing it to a production environment. + + +## Configuration Parameters + +This chart is configured via the `values.yaml` file. Below is a detailed description of each available parameter. + +### Application Image + +| Parameter | Description | Default Value | +| :--- | :--- | :--- | +| `replicaCount` | The number of pygeoapi replicas. Increase for higher availability and processing capacity. | `1` | +| `image.repository` | The Docker image repository for pygeoapi to be used. | `docker.io/ndscprm/pygeoapi` | +| `image.pullPolicy` | The image pull policy. `IfNotPresent` prevents downloading if the image already exists locally. | `IfNotPresent` | +| `image.tag` | The specific Docker image tag for the pygeoapi version. | `"0.21.0-rootless"` | +| `imagePullSecrets` | A list of authentication secrets for pulling images from private repositories. | `[]` | +| `nameOverride` | Overrides the chart name when creating Kubernetes resources. | `""` | +| `fullnameOverride` | Overrides the full chart name when creating Kubernetes resources. | `""` | + +* This helm chart uses a modified pygeoapi image, because of OpenShift's security requirements. To use official pygeoapi image (v0.21.0), it's required to run the container as root. + +### Service Account + +| Parameter | Description | Default Value | +| :--- | :--- | :--- | +| `serviceAccount.create` | If `true`, a Service Account is created for the application. | `true` | +| `serviceAccount.automount` | If `true`, automatically mounts the Service Account's credentials. | `true` | +| `serviceAccount.annotations` | Annotations to be added to the Service Account. | `{}` | +| `serviceAccount.name` | The name of the Service Account to use. If empty, a name is generated. | `""` | + +### Pod Security and Context + +| Parameter | Description | Default Value | +| :--- | :--- | :--- | +| `podAnnotations` | Annotations to be added to the Pod. | `{}` | +| `podLabels` | Labels to be added to the Pod. | `{}` | +| `podSecurityContext` | The security context for the Pod. For example, `fsGroup` to define the filesystem group ID. | `{}` | +| `securityContext` | The security context for the container. For example, `runAsNonRoot` to ensure the process does not run as root. | `{}` | + +### Service and Exposure + +| Parameter | Description | Default Value | +| :--- | :--- | :--- | +| `service.type` | The Kubernetes service type to expose pygeoapi. | `ClusterIP` | +| `service.port` | The service port where pygeoapi will be exposed. | `8080` | + +### Ingress + +| Parameter | Description | Default Value | +| :--- | :--- | :--- | +| `ingress.enabled` | If `true`, an Ingress is created to expose pygeoapi externally. | `false` | +| `ingress.className` | The name of the Ingress class to be used. | `""` | +| `ingress.annotations` | Additional annotations for the Ingress. Useful for integrations like `cert-manager`. | `{}` | +| `ingress.hosts` | A list of hosts and paths to route traffic to pygeoapi. | `[]` | +| `ingress.tls` | A list of TLS secrets and associated hosts to encrypt traffic. | `[]` | + +### pygeoapi Settings + +| Parameter | Description | Default Value | +| :--- | :--- | :--- | +| `settings.config.enabled` | If `true`, mounts the pygeoapi configuration file (`local.config.yml`). | `true` | +| `settings.config.name` | The name of the ConfigMap or Secret containing the configuration. | `pygeoapi-config` | +| `settings.config.isEncrypted` | If `true`, reads the configuration from a Secret; otherwise, from a ConfigMap. | `false` | +| `settings.config.mountPath` | The full path where the configuration file will be mounted inside the container. | `/pygeoapi/local.config.yml` | +| `settings.config.readOnly` | If `true`, mounts the file as read-only. | `true` | +| `settings.openapi` | The path to the pygeoapi OpenAPI file. | `/tmp/pygeoapi-openapi.yml` | +| `settings.serverAdmin` | Enables or disables the server's administration interface. | `false` | +| `settings.hotReload` | Enables hot-reloading of the server. | `false` | +| `settings.wsgi.workers` | The number of workers for the WSGI server (Gunicorn). | `3` | +| `settings.wsgi.workerTimeout` | The worker timeout (in milliseconds). | `6000` | +| `settings.wsgi.workerClass` | The worker class to be used by the WSGI server. | `gevent` | + +### Resources and Scaling + +| Parameter | Description | Default Value | +| :--- | :--- | :--- | +| `resources` | Definition of resource limits and requests (CPU and memory) for the container. | `{}` | +| `autoscaling.enabled` | If `true`, enables the Horizontal Pod Autoscaler (HPA) to manage the number of replicas. | `false` | +| `autoscaling.minReplicas` | The minimum number of replicas when autoscaling is enabled. | `1` | +| `autoscaling.maxReplicas` | The maximum number of replicas when autoscaling is enabled. | `5` | +| `autoscaling.targetCPUUtilizationPercentage` | The CPU utilization percentage the HPA will try to maintain. | `80` | +| `autoscaling.targetMemoryUtilizationPercentage` | The memory utilization percentage the HPA will try to maintain. | `80` | + +### Additional Volumes + +| Parameter | Description | Default Value | +| :--- | :--- | :--- | +| `volumes` | A list of additional volumes to be added to the Deployment definition. | `[]` | +| `volumeMounts` | A list of additional volume mounts for the containers. | `[]` | + +### Scheduling Configurations + +| Parameter | Description | Default Value | +| :--- | :--- | :--- | +| `nodeSelector` | Node labels for selecting which nodes the Pod will be scheduled on. | `{}` | +| `tolerations` | Tolerations to schedule the Pod on nodes with taints. | `[]` | +| `affinity` | Affinity and anti-affinity rules to control Pod scheduling. | `{}` | + +For a complete list of all parameters and their default values, please refer to the `values.yaml` file in the chart repository. + diff --git a/helm/deploys/values.crc.yaml b/helm/deploys/values.crc.yaml new file mode 100644 index 0000000..fb69c4c --- /dev/null +++ b/helm/deploys/values.crc.yaml @@ -0,0 +1,31 @@ +ingress: + enabled: true + hosts: + - host: "ogcapi.pygeoapi.apps-crc.testing" + paths: + - path: / + pathType: ImplementationSpecific + +serviceAccount: + create: true + name: pygeoapi + +settings: + config: + enabled: true + name: pygeoapi-custom-config + isEncrypted: true # false -> configMap, true -> secret + # mountPath: /pygeoapi/local.config.yml + # readOnly: true + # openapi: /tmp/pygeoapi-openapi.yml + serverAdmin: false + hotReload: false + wsgi: + workers: 3 + workerTimeout: 6000 + workerClass: gevent + +# resources: +# limits: +# cpu: 1 +# # memory: 128Mi diff --git a/helm/deploys/values.minikube.yaml b/helm/deploys/values.minikube.yaml new file mode 100644 index 0000000..879099e --- /dev/null +++ b/helm/deploys/values.minikube.yaml @@ -0,0 +1,43 @@ +ingress: + enabled: true + hosts: + - host: "ogcapi-pygeoapi.minikube.local" # Change to defined minikube ingress URL + paths: + - path: / + pathType: ImplementationSpecific + +# overrides image: registry and tag. Default is official image, last numbered release +image: + pullPolicy: Always # force to pull image on every deploy + tag: latest # force to pull latest + +service: + type: NodePort # minikube single node + +# securityContext: +# # capabilities: +# # drop: +# # - ALL +# # readOnlyRootFilesystem: true +# runAsNonRoot: true +# runAsUser: 1000 +# runAsGroup: 0 + +settings: + config: + enabled: true + name: pygeoapi-test + isEncrypted: true # false -> configMap, true -> secret + # mountPath: /pygeoapi/local.config.yml + # readOnly: true + serverAdmin: false + hotReload: True + wsgi: + workers: 5 + workerTimeout: 6000 + workerClass: gevent + +# resources: +# limits: +# cpu: 1 +# # memory: 128Mi diff --git a/helm/templates/NOTES.txt b/helm/templates/NOTES.txt new file mode 100644 index 0000000..f2b714c --- /dev/null +++ b/helm/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 }}{{ .path }} + {{- 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 "pygeoapi.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 "pygeoapi.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "pygeoapi.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 "pygeoapi.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/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl new file mode 100644 index 0000000..c5a4e58 --- /dev/null +++ b/helm/templates/_helpers.tpl @@ -0,0 +1,73 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "pygeoapi.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 "pygeoapi.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 "pygeoapi.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "pygeoapi.labels" -}} +helm.sh/chart: {{ include "pygeoapi.chart" . }} +{{ include "pygeoapi.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "pygeoapi.selectorLabels" -}} +app.kubernetes.io/name: {{ include "pygeoapi.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "pygeoapi.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "pygeoapi.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +# root path handler +{{- define "pygeoapi.domain" -}} +{{- $serviceHost := printf "%s.%s.svc.cluster.local:%s" (include "pygeoapi.fullname" .) .Release.Namespace (.Values.service.port | toString) -}} +{{- $ingressHost := (first .Values.ingress.hosts).host -}} +{{ ternary $ingressHost $serviceHost .Values.ingress.enabled }} +{{- end }} + +{{- define "pygeoapi.subPath" -}} +{{ (first (first .Values.ingress.hosts).paths).path }} +{{- end }} \ No newline at end of file diff --git a/helm/templates/configmap.yaml b/helm/templates/configmap.yaml new file mode 100644 index 0000000..f11011d --- /dev/null +++ b/helm/templates/configmap.yaml @@ -0,0 +1,18 @@ +{{- $rootPath := (include "pygeoapi.domain" .) -}} +{{- $context := (include "pygeoapi.subPath" .) -}} +{{- $prefix := ternary "http" "https" (empty .Values.ingress.tls) -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "pygeoapi.fullname" . }}-env +data: + SCRIPT_NAME: {{ $context | quote }} + PYGEOAPI_CONFIG: {{ .Values.settings.config.mountPath | quote }} # FIXME: look into pygeoapi entrypoint, this env never changes app behaviour + PYGEOAPI_OPENAPI: {{ .Values.settings.openapi | quote }} # FIXME: look into pygeoapi entrypoint, this env never changes app behaviour + CONTAINER_PORT: {{ .Values.service.port | toString | quote }} + WSGI_WORKERS: {{ .Values.settings.wsgi.workers | default 4 | toString | quote }} + WSGI_WORKER_TIMEOUT: {{ .Values.settings.wsgi.workerTimeout | default 6000 | toString | quote }} + WSGI_WORKER_CLASS: {{ .Values.settings.wsgi.workerClass | default "gevent" }} + # only works with local.config.yml + PYGEOAPI_SERVER_URL: {{ printf "%s://%s%s" $prefix $rootPath $context | quote }} + PYGEOAPI_SERVER_ADMIN: {{ .Values.settings.serverAdmin | toString | quote }} diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml new file mode 100644 index 0000000..a0e114d --- /dev/null +++ b/helm/templates/deployment.yaml @@ -0,0 +1,104 @@ +{{- $fullName := include "pygeoapi.fullname" . -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "pygeoapi.fullname" . }} + labels: + {{- include "pygeoapi.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "pygeoapi.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "pygeoapi.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "pygeoapi.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- if .Values.settings.hotReload }} + args: + - run-with-hot-reload + {{- end }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + envFrom: + - configMapRef: + name: {{ include "pygeoapi.fullname" . }}-env + livenessProbe: + httpGet: + path: {{ (include "pygeoapi.subPath" .) }} + port: http + initialDelaySeconds: 60 + # readinessProbe: + # httpGet: + # path: {{ (include "pygeoapi.subPath" .) }} + # port: http + # initialDelaySeconds: 60 + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + {{- with .Values.volumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.settings.config }} + {{- if .enabled }} + {{- $mountPath := .mountPath | default "/pygeoapi/local.config.yml" }} + - name: pygeoapi-config + mountPath: {{ $mountPath }} + subPath: {{ base $mountPath }} + readOnly: {{ .readOnly | default true }} + {{- end }} + {{- end }} + volumes: + {{- with .Values.volumes }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.settings.config }} + {{- if .enabled }} + {{- $resourceName := .name | default (printf "%s-config" $fullName) }} + - name: pygeoapi-config + {{- if .isEncrypted }} + secret: + secretName: {{ $resourceName }} + {{- else }} + configMap: + name: {{ $resourceName }} + {{- end }} + {{- end }} + {{- 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 }} diff --git a/helm/templates/hpa.yaml b/helm/templates/hpa.yaml new file mode 100644 index 0000000..af172a8 --- /dev/null +++ b/helm/templates/hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "pygeoapi.fullname" . }} + labels: + {{- include "pygeoapi.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "pygeoapi.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/helm/templates/ingress.yaml b/helm/templates/ingress.yaml new file mode 100644 index 0000000..8a2b265 --- /dev/null +++ b/helm/templates/ingress.yaml @@ -0,0 +1,61 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "pygeoapi.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- 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 "pygeoapi.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/helm/templates/secret.yaml b/helm/templates/secret.yaml new file mode 100644 index 0000000..e69de29 diff --git a/helm/templates/service.yaml b/helm/templates/service.yaml new file mode 100644 index 0000000..18d8574 --- /dev/null +++ b/helm/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "pygeoapi.fullname" . }} + labels: + {{- include "pygeoapi.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "pygeoapi.selectorLabels" . | nindent 4 }} diff --git a/helm/templates/serviceaccount.yaml b/helm/templates/serviceaccount.yaml new file mode 100644 index 0000000..1e2cdff --- /dev/null +++ b/helm/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "pygeoapi.serviceAccountName" . }} + labels: + {{- include "pygeoapi.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/helm/templates/tests/test-connection.yaml b/helm/templates/tests/test-connection.yaml new file mode 100644 index 0000000..4746608 --- /dev/null +++ b/helm/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "pygeoapi.fullname" . }}-test-connection" + labels: + {{- include "pygeoapi.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "pygeoapi.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/helm/values.yaml b/helm/values.yaml new file mode 100644 index 0000000..536b284 --- /dev/null +++ b/helm/values.yaml @@ -0,0 +1,113 @@ +# Default values for pygeoapi. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: docker.io/geopython/pygeoapi + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Automatically mount a ServiceAccount's API credentials? + automount: true + # 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: "" + +podAnnotations: {} +podLabels: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 8080 + +ingress: + enabled: false + className: "" + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: pygeoapi.apps-crc.testing + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +settings: + config: + enabled: true + name: pygeoapi-config + isEncrypted: false # false -> configMap, true -> secret + mountPath: /pygeoapi/local.config.yml + readOnly: true + openapi: /tmp/pygeoapi-openapi.yml + serverAdmin: false + hotReload: false + wsgi: + workers: 3 + workerTimeout: 6000 + workerClass: gevent + +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 + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 5 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +# Additional volumes on the output Deployment definition. +volumes: [] +# - name: foo +# secret: +# secretName: mysecret +# optional: false + +# Additional volumeMounts on the output Deployment definition. +volumeMounts: [] +# - name: foo +# mountPath: "/etc/foo" +# readOnly: true + +nodeSelector: {} + +tolerations: [] + +affinity: {}