From 0a85027927b4eb52add4661be272b0ba9e34e345 Mon Sep 17 00:00:00 2001 From: Etienne Date: Fri, 17 Apr 2026 13:44:42 -0400 Subject: [PATCH 1/3] Update README with upstream contribution links Added references to upstream contribution and resources. --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 5a07bf4..7b77d1f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ +Refer to https://github.com/resourcespace/docker + +This is a temporary for for upstream contribution to https://github.com/resourcespace/docker + + # resourcespace/docker The official Docker image for ResourceSpace. Full build instructions can be found on our [Knowledge Base](https://www.resourcespace.com/knowledge-base/systemadmin/install_docker). From 2c56cfd1a2264f331b834a35e03330d1fe4ec80b Mon Sep 17 00:00:00 2001 From: dap012 Date: Mon, 20 Apr 2026 11:07:57 -0600 Subject: [PATCH 2/3] Add helm chart, image modifications, config and license --- 000-default.conf | 16 +++ Dockerfile | 38 +++---- NOTICE | 52 ++++++++++ README.md | 163 ++++++++++++++++++++++++++++-- entrypoint.sh | 30 ++++-- helm/Chart.yaml | 16 +++ helm/LICENSE | 40 ++++++++ helm/README.md | 146 ++++++++++++++++++++++++++ helm/templates/_helpers.tpl | 70 +++++++++++++ helm/templates/deployment.yaml | 134 ++++++++++++++++++++++++ helm/templates/mariadb.yaml | 129 +++++++++++++++++++++++ helm/templates/pvc-filestore.yaml | 20 ++++ helm/templates/route.yaml | 37 +++++++ helm/templates/secret.yaml | 27 +++++ helm/values.yaml | 108 ++++++++++++++++++++ ports.conf | 5 + 16 files changed, 1001 insertions(+), 30 deletions(-) create mode 100644 000-default.conf create mode 100755 NOTICE create mode 100755 helm/Chart.yaml create mode 100755 helm/LICENSE create mode 100755 helm/README.md create mode 100755 helm/templates/_helpers.tpl create mode 100755 helm/templates/deployment.yaml create mode 100755 helm/templates/mariadb.yaml create mode 100755 helm/templates/pvc-filestore.yaml create mode 100755 helm/templates/route.yaml create mode 100755 helm/templates/secret.yaml create mode 100755 helm/values.yaml create mode 100644 ports.conf diff --git a/000-default.conf b/000-default.conf new file mode 100644 index 0000000..225e7e2 --- /dev/null +++ b/000-default.conf @@ -0,0 +1,16 @@ +# OpenShift-compatible default vhost. +# Replaces /etc/apache2/sites-enabled/000-default.conf in the image build. +ServerName resourcespace + + + DocumentRoot /var/www/html + + + Options FollowSymLinks + AllowOverride All + Require all granted + + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 60c43e9..5628ae1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,33 +31,37 @@ RUN apt-get update && apt-get install -y \ python3-opencv \ python3 \ python3-pip \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* RUN sed -i -e "s/upload_max_filesize\s*=\s*2M/upload_max_filesize = 100M/g" /etc/php/8.3/apache2/php.ini \ - && sed -i -e "s/post_max_size\s*=\s*8M/post_max_size = 100M/g" /etc/php/8.3/apache2/php.ini \ - && sed -i -e "s/max_execution_time\s*=\s*30/max_execution_time = 300/g" /etc/php/8.3/apache2/php.ini \ - && sed -i -e "s/memory_limit\s*=\s*128M/memory_limit = 1G/g" /etc/php/8.3/apache2/php.ini + && sed -i -e "s/post_max_size\s*=\s*8M/post_max_size = 100M/g" /etc/php/8.3/apache2/php.ini \ + && sed -i -e "s/max_execution_time\s*=\s*30/max_execution_time = 300/g" /etc/php/8.3/apache2/php.ini \ + && sed -i -e "s/memory_limit\s*=\s*128M/memory_limit = 1G/g" /etc/php/8.3/apache2/php.ini -RUN printf '\n\ -\tOptions FollowSymLinks\n\ -\n'\ ->> /etc/apache2/sites-enabled/000-default.conf +# OpenShift: replace upstream Apache configs with port-8080 versions +COPY ports.conf /etc/apache2/ports.conf +COPY 000-default.conf /etc/apache2/sites-enabled/000-default.conf ADD cronjob /etc/cron.daily/resourcespace WORKDIR /var/www/html RUN rm -f index.html \ - && svn co -q https://svn.resourcespace.com/svn/rs/releases/10.7 . \ - && mkdir -p filestore \ - && chmod 777 filestore \ - && chmod -R 777 include/ - + && svn co -q https://svn.resourcespace.com/svn/rs/releases/10.7 . \ + && mkdir -p filestore \ + && chmod 777 filestore \ + && chmod -R 777 include/ -# Copy custom entrypoint script +# OpenShift: make all runtime dirs world-writable so an arbitrary UID can write +RUN mkdir -p /var/run/apache2 /var/lock/apache2 /var/log/apache2 \ + && chmod -R 777 /var/run/apache2 /var/lock/apache2 /var/log/apache2 \ + && chmod -R 777 /var/www/html + +# OpenShift-patched entrypoint (no cron, port 8080, tmp runtime dirs) COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh -# Start both cron and Apache -CMD ["/entrypoint.sh"] +EXPOSE 8080 + +CMD ["/entrypoint.sh"] \ No newline at end of file diff --git a/NOTICE b/NOTICE new file mode 100755 index 0000000..6a4112d --- /dev/null +++ b/NOTICE @@ -0,0 +1,52 @@ +------------------------------------------------------------------------------- +Crown Copyright and Upstream Licensing +Droit d'auteur de la Couronne et licences en amont +------------------------------------------------------------------------------- + +English + +Contributions produced by GC and submitted to upstream open-source application +repositories are provided under the licence of the upstream repository, in +accordance with its contribution and governance model. + +Notwithstanding the above, Crown copyright is retained for the portions of code +authored by public servants, in the form and state in which those contributions +are submitted. + +This retention does not restrict or alter the rights granted to users under the +applicable upstream open-source licence. + +Français + +Les contributions produites par le GC et soumises à des dépôts d'applications +open source en amont sont diffusées sous la licence du dépôt en amont, +conformément à ses règles de contribution et de gouvernance. + +Nonobstant ce qui précède, le droit d'auteur de la Couronne est conservé pour +les portions de code rédigées par des fonctionnaires, dans la forme et l'état +dans lesquels ces contributions sont soumises. + +Cette conservation n'a pas pour effet de restreindre ou de modifier les droits +accordés aux utilisateurs par la licence open source en amont applicable. + +------------------------------------------------------------------------------- + +English + +Portions of this code were authored by the Government of Canada. These +components, in the form contributed by the GC, are © His Majesty the King in +Right of Canada, as represented by the Department of Agriculture and Agri-Food +Canada. This attribution does not modify or replace the applicable licence, +does not affect permissions, conditions, or disclaimers, and does not +constitute an endorsement by the Government of Canada of the software, the +repository, or any deployed application. + +Français + +Certaines portions de ce code ont été rédigées par le gouvernement du Canada. +Ces composantes, dans la forme contribuée par le GC, sont © Sa Majesté le Roi +du chef du Canada, représenté par le ministère de l'Agriculture et +Agroalimentaire Canada. Cette mention ne modifie ni ne remplace la licence +applicable, n'affecte pas les autorisations, conditions ou limitations de +responsabilité, et ne constitue pas une approbation du logiciel, du dépôt ou +des applications déployées par le gouvernement du Canada. diff --git a/README.md b/README.md index 7b77d1f..0c40f80 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,160 @@ -Refer to https://github.com/resourcespace/docker +# ResourceSpace Helm Chart -This is a temporary for for upstream contribution to https://github.com/resourcespace/docker +Deploys [ResourceSpace](https://www.resourcespace.com/) (Digital Asset Management) on +OpenShift with: +- **ResourceSpace** web app as a Kubernetes `Deployment` +- **MariaDB** as an in-cluster `StatefulSet` +- **External NFS storage** for both the filestore and MariaDB data +- **OpenShift Route** with edge TLS termination -# resourcespace/docker -The official Docker image for ResourceSpace. Full build instructions can be found on our [Knowledge Base](https://www.resourcespace.com/knowledge-base/systemadmin/install_docker). +--- -# Installation notes -* Before building the Docker image, change the db.env file replacing the default "change-me" passwords to secure values. -* When setting up ResourceSpace ensure you enter "mariadb" as the MySQL server instead of "localhost" and leave the "MySQL binary path" empty. +## Prerequisites + +| Requirement | Notes | +|---|---| +| OpenShift 4.x | Tested on 4.12+ | +| Helm 3.x | `helm version` | +| `oc` CLI logged in | `oc whoami` | + +--- + +## Image Build +The image should be built from the official source repository and pushed to an available registry +### Source +```bash +git clone git@github.com:resourcespace/docker.git # for SSH clone +cd docker +``` +### OpenShift Modifications +The upstream image runs apache on port 80 as root, which OpenShift's `restricted-v2` SSC does not permit. Three files should be added/modified before building: +`ports.conf` - Tells Apache to listen on port 8080 instead: +```bash +Listen 8080 +``` +`000-default.conf` - vhost on port 8080 with correct directory permissions: +```bash +ServerName resourcespace + + + DocumentRoot /var/www/html + + + Options FollowSymLinks + AllowOverride All + Require all granted + + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + +``` +`entrypoint.sh` - skips cron (no `/var/run` write access), redirects apache runtime files to `/tmp` which is writable by any UID: +```bash +#!/bin/bash +set -e + +mkdir -p /tmp/apache2/run /tmp/apache2/lock /tmp/apache2/log +export APACHE_RUN_DIR=/tmp/apache2/run +export APACHE_LOCK_DIR=/tmp/apache2/lock +export APACHE_LOG_DIR=/tmp/apache2/log +export APACHE_PID_FILE=/tmp/apache2/run/apache2.pid + +exec apachectl -D FOREGROUND +``` +`Dockerfile` - additions to upstream: +```bash +# Replace Apache configs before the SVN checkout +COPY ports.conf /etc/apache2/ports.conf +COPY 000-default.conf /etc/apache2/sites-enabled/000-default.conf + +# Make runtime dirs world-writable for arbitrary UID +RUN mkdir -p /var/run/apache2 /var/lock/apache2 /var/log/apache2 \ + && chmod -R 777 /var/run/apache2 /var/lock/apache2 /var/log/apache2 \ + && chmod -R 777 /var/www/html + +EXPOSE 8080 +``` +### Build and Push +```bash +docker build -t /: . +docker push /: +``` + +## Helm Chart +### Config before deploying +In `values.yaml`: +```yaml +resourcespace: + image: / + tag: + pullPolicy: + + hostname: # Example: resourcespace.apps.mycluster.example.com +mariadb: + auth: + rootPassword: "" # set in advance or at runtime + database: "resourcespace" + username: "resourcespace" + password: "" # set in advance or at runtime +``` +### Install +```bash +oc new-project # skip if namespace already exists +helm install resourcespace . -n # run in dir where values.yaml is + +# install with secret creation +helm install resourcespace . -n \ + --set mariadb.auth.rootPassword= \ + --set mariadb.auth.password= +``` + +### Upgrade +```bash +helm upgrade resourcespace . -n # also where values.yaml is +``` + +### Uninstall +```bash +helm uninstall resourcespace -n +``` + +## Run Setup Wizard + +On first deployment, navigating to the route URL shows the ResourceSpace setup wizard. + +### Known Issue — Base URL Check + +The wizard validates the base URL by fetching `license.txt` from it. This fails when +using the public route URL because the pod cannot route back to itself through the +external ingress. **Workaround:** enter the internal service URL during setup: +``` +http://resourcespace +``` +The entrypoint automatically corrects this to the public route URL on every startup +via the `RS_BASEURL` environment variable — no manual fix needed. + +### Database Settings + +| Field | Value | +|---|---| +| MySQL server | `resourcespace-mariadb` | +| MySQL port | `3306` | +| MySQL database | value of `mariadb.auth.database` in values.yaml | +| MySQL username | value of `mariadb.auth.username` in values.yaml | +| MySQL password | value of `mariadb.auth.password` in values.yaml | +| MySQL binary path | *(leave empty)* | +| Filestore path | `/var/www/html/filestore` | + +### Persisting Configuration + +After completing the wizard, restart the pod to persist `config.php` and apply the +correct public route URL automatically: + +```bash +oc rollout restart deployment/resourcespace -n +``` + +The entrypoint saves `config.php` to the filestore PVC on first run and restores it +with the correct `RS_BASEURL` on every subsequent restart. \ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh index d1f4ebe..03b91ae 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,10 +1,28 @@ #!/bin/bash -# Start cron service -service cron start +mkdir -p /tmp/apache2/run /tmp/apache2/lock /tmp/apache2/log +export APACHE_RUN_DIR=/tmp/apache2/run +export APACHE_LOCK_DIR=/tmp/apache2/lock +export APACHE_LOG_DIR=/tmp/apache2/log +export APACHE_PID_FILE=/tmp/apache2/run/apache2.pid -# Ensure daily cron jobs are executable -chmod +x /etc/cron.daily/* +CONFIG_PVC="/var/www/html/filestore/config.php" +CONFIG_DST="/var/www/html/include/config.php" -# Start Apache in the foreground (keeps the container alive) -apachectl -D FOREGROUND +if [ -f "$CONFIG_PVC" ] && [ -s "$CONFIG_PVC" ]; then + # Subsequent starts: restore config and fix baseurl + echo "[entrypoint] Restoring config.php from filestore..." + cp "$CONFIG_PVC" "$CONFIG_DST" + if [ -n "$RS_BASEURL" ]; then + echo "[entrypoint] Fixing baseurl to $RS_BASEURL" + sed -i "s|^\$baseurl\s*=\s*'[^']*';|\$baseurl = '$RS_BASEURL';|g" "$CONFIG_DST" + fi +else + # First run: symlink config.php into the PVC so wizard writes directly there + echo "[entrypoint] First run — symlinking config.php to filestore PVC..." + rm -f "$CONFIG_DST" + ln -s "$CONFIG_PVC" "$CONFIG_DST" + echo "[entrypoint] Wizard will write config.php directly to the PVC." +fi + +exec apachectl -D FOREGROUND \ No newline at end of file diff --git a/helm/Chart.yaml b/helm/Chart.yaml new file mode 100755 index 0000000..e91d395 --- /dev/null +++ b/helm/Chart.yaml @@ -0,0 +1,16 @@ +apiVersion: v2 +name: resourcespace +description: > + Helm chart for deploying ResourceSpace (DAM) on OpenShift with + an in-cluster MariaDB StatefulSet +type: application +version: 0.1.0 +appVersion: "10.7" +keywords: + - resourcespace + - dam + - media +sources: + - https://github.com/resourcespace/docker +maintainers: + - name: your-team diff --git a/helm/LICENSE b/helm/LICENSE new file mode 100755 index 0000000..18e0234 --- /dev/null +++ b/helm/LICENSE @@ -0,0 +1,40 @@ +MIT License + +© His Majesty the King in Right of Canada, as represented by the Minister of +Agriculture and Agri-Food Canada, 2026. + +© Sa Majesté le Roi du chef du Canada, représentée par le ministre de +l'Agriculture et Agroalimentaire Canada, 2026. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- + +English + +Deployed applications are obtained from their respective upstream projects and +are governed by their own, separate licenses, including but not limited to +permissive, copyleft (GPL/AGPL), and proprietary licenses. + +Français + +Les applications déployées sont obtenues à partir de leurs projets amont +respectifs et sont régies par leurs propres licences distinctes, y compris, +sans s'y limiter, des licences permissives, à copyleft (GPL/AGPL) et +propriétaires. diff --git a/helm/README.md b/helm/README.md new file mode 100755 index 0000000..175085a --- /dev/null +++ b/helm/README.md @@ -0,0 +1,146 @@ +# ResourceSpace Helm Chart + +Deploys [ResourceSpace](https://www.resourcespace.com/) (Digital Asset Management) on +OpenShift with: + +- **ResourceSpace** web app as a Kubernetes `Deployment` +- **MariaDB** as an in-cluster `StatefulSet` +- **External NFS storage** for both the filestore and MariaDB data +- **OpenShift Route** with edge TLS termination + +--- + +## Prerequisites + +| Requirement | Notes | +|---|---| +| OpenShift 4.x | Tested on 4.12+ | +| Helm 3.x | `helm version` | +| `oc` CLI logged in | `oc whoami` | + +--- + +## Image Build +ResourceSpace does not publish a pre-built image. The image must be built from the official source repository and pushed to an internal registry +### Source +```bash +git clone git@github.com:resourcespace/docker.git # for SSH clone +cd docker +``` +### OpenShift Modifications +The upstream image runs apache on port 80 as root, which OpenShift's `restricted-v2` SSC does not permit. Three files should be added/modified before building: +`ports.conf` - Tells Apache to listen on port 8080 instead: +```bash +Listen 8080 +``` +`000-default.conf` - vhost on port 8080 with correct directory permissions: +```bash +ServerName resourcespace + + + DocumentRoot /var/www/html + + + Options FollowSymLinks + AllowOverride All + Require all granted + + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + +``` +`entrypoint.sh` - skips cron (no `/var/run` write access), redirects apache runtime files to `/tmp` which is writable by any UID: +```bash +#!/bin/bash +set -e + +mkdir -p /tmp/apache2/run /tmp/apache2/lock /tmp/apache2/log +export APACHE_RUN_DIR=/tmp/apache2/run +export APACHE_LOCK_DIR=/tmp/apache2/lock +export APACHE_LOG_DIR=/tmp/apache2/log +export APACHE_PID_FILE=/tmp/apache2/run/apache2.pid + +exec apachectl -D FOREGROUND +``` +`Dockerfile` - additions to upstream: +```bash +# Replace Apache configs before the SVN checkout +COPY ports.conf /etc/apache2/ports.conf +COPY 000-default.conf /etc/apache2/sites-enabled/000-default.conf + +# Make runtime dirs world-writable for arbitrary UID +RUN mkdir -p /var/run/apache2 /var/lock/apache2 /var/log/apache2 \ + && chmod -R 777 /var/run/apache2 /var/lock/apache2 /var/log/apache2 \ + && chmod -R 777 /var/www/html + +EXPOSE 8080 +``` +### Build and Push +```bash +docker build -t /: . +docker push /: +``` + +## Helm Chart +### Config before deploying +In `values.yaml`: +```yaml +resourcespace: + image: / + tag: + pullPolicy: + + hostname: # Example: resourcespace.apps.mycluster.example.com +mariadb: + auth: + rootPassword: "" + database: "resourcespace" + username: "resourcespace" + password: "" +``` +### Install +```bash +oc new-project # skip if namespace already exists +helm install resourcespace . -n # run in dir where values.yaml is +``` + +### Upgrade +```bash +helm upgrade resourcespace . -n # also where values.yaml is +``` + +### Uninstall +```bash +helm uninstall resourcespace -n +``` + +## Run Setup Wizard +On first deployment, navigating to the route URL shows the ResourceSpace setup wizard +### Known Issue - Base URL Check +The wizard validates the base URL by fetching `license.txt` from it. This fails when using the public route URL because the pod cannot route back to itself through the external ingress. **Workaround:** Enter the internal service URL during setup: +``` +http://resourcespace +``` +### Database Settings +| Field | Value | +|---|---| +| MySQL server | `resourcespace-mariadb | +| MySQL port | `3306` | +| MySQL database | values of `mariadb.auth.database` | +| MySQL username | values of `mariadb.auth.username` | +| MySQL password | values of `mariadb.auth.password` | +| MySQL binary path | (leave empty) | +| Filestore path | `/var/www/htlm/filestore` | + +### Fix Base URL after setup +After completing the wizard, the internal URL will have been written to `config.php`. Fix it to the public route URL: +```bash +oc exec -n deployment/resourcespace -- \ + sed -i "s||g" \ + /var/www/html/include/config.php + +# Verify +oc exec -n deployment/resourcespace -- \ + grep baseurl /var/www/html/include/config.php +``` \ No newline at end of file diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl new file mode 100755 index 0000000..a38387e --- /dev/null +++ b/helm/templates/_helpers.tpl @@ -0,0 +1,70 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "resourcespace.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "resourcespace.fullname" -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels applied to every resource. +*/}} +{{- define "resourcespace.labels" -}} +helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Selector labels for the ResourceSpace Deployment. +*/}} +{{- define "resourcespace.selectorLabels" -}} +app.kubernetes.io/name: resourcespace +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Selector labels for the MariaDB StatefulSet. +*/}} +{{- define "mariadb.selectorLabels" -}} +app.kubernetes.io/name: mariadb +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Name of the Secret holding database credentials. +*/}} +{{- define "mariadb.secretName" -}} +{{ include "resourcespace.fullname" . }}-mariadb-secret +{{- end }} + +{{/* +Internal DNS name of the MariaDB Service (used by ResourceSpace config.php). +*/}} +{{- define "mariadb.serviceName" -}} +{{ include "resourcespace.fullname" . }}-mariadb +{{- end }} + +{{/* +Validate required values are set before deploying. +*/}} +{{- define "resourcespace.validateValues" -}} +{{- if not .Values.mariadb.auth.rootPassword }} + {{- fail "ERROR: mariadb.auth.rootPassword must be set. Use --set mariadb.auth.rootPassword=" }} +{{- end }} +{{- if not .Values.mariadb.auth.password }} + {{- fail "ERROR: mariadb.auth.password must be set. Use --set mariadb.auth.password=" }} +{{- end }} +{{- if not .Values.resourcespace.hostname }} + {{- fail "ERROR: resourcespace.hostname must be set. Use --set resourcespace.hostname=" }} +{{- end }} +{{- if not .Values.resourcespace.image.repository }} + {{- fail "ERROR: resourcespace.image.repository must be set. Build the image from https://github.com/resourcespace/docker and push to your registry." }} +{{- end }} +{{- end }} diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml new file mode 100755 index 0000000..fe893ef --- /dev/null +++ b/helm/templates/deployment.yaml @@ -0,0 +1,134 @@ +# ============================================================================= +# ResourceSpace Deployment +# ============================================================================= +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "resourcespace.fullname" . }} + namespace: {{ .Values.global.namespace }} + labels: + {{- include "resourcespace.labels" . | nindent 4 }} + {{- include "resourcespace.selectorLabels" . | nindent 4 }} +spec: + replicas: {{ .Values.resourcespace.replicaCount }} + selector: + matchLabels: + {{- include "resourcespace.selectorLabels" . | nindent 6 }} + # Recreate ensures the NFS filestore isn't written to by two pods + # simultaneously when replicaCount=1. Change to RollingUpdate only + # after validating concurrent NFS access with multiple replicas. + strategy: + type: Recreate + template: + metadata: + labels: + {{- include "resourcespace.labels" . | nindent 8 }} + {{- include "resourcespace.selectorLabels" . | nindent 8 }} + spec: + # OpenShift assigns a UID from the namespace range automatically. + # No securityContext set — pods run under restricted-v2 SCC by default. + initContainers: + - name: wait-for-mariadb + image: busybox:1.36 + command: + - sh + - -c + - | + until nc -z {{ include "mariadb.serviceName" . }} 3306; do + echo "Waiting for MariaDB..."; sleep 3; + done + echo "MariaDB is up." + + + containers: + - name: resourcespace + image: "{{ .Values.resourcespace.image.repository }}:{{ .Values.resourcespace.image.tag }}" + imagePullPolicy: {{ .Values.resourcespace.image.pullPolicy }} + ports: + - name: http + containerPort: 8080 + protocol: TCP + env: + # Database connection - consumed by the entrypoint / config.php. + # The hostname resolves to the MariaDB ClusterIP Service. + - name: RS_DB_HOST + value: {{ include "mariadb.serviceName" . | quote }} + - name: RS_DB_NAME + valueFrom: + secretKeyRef: + name: {{ include "mariadb.secretName" . }} + key: MARIADB_DATABASE + - name: RS_DB_USER + valueFrom: + secretKeyRef: + name: {{ include "mariadb.secretName" . }} + key: MARIADB_USER + - name: RS_DB_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "mariadb.secretName" . }} + key: MARIADB_PASSWORD + # Base URL - used by ResourceSpace for link generation. + - name: RS_BASEURL + value: "https://{{ .Values.resourcespace.hostname }}" + {{- with .Values.resourcespace.extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: filestore + mountPath: /var/www/html/filestore + resources: + {{- toYaml .Values.resourcespace.resources | nindent 12 }} + livenessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 60 + periodSeconds: 15 + failureThreshold: 4 + readinessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 30 + periodSeconds: 10 + + volumes: + - name: filestore + persistentVolumeClaim: + claimName: {{ include "resourcespace.fullname" . }}-filestore-pvc + + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + +--- +# ============================================================================= +# ResourceSpace ClusterIP Service +# Exposed externally via the OpenShift Route below. +# ============================================================================= +apiVersion: v1 +kind: Service +metadata: + name: {{ include "resourcespace.fullname" . }} + namespace: {{ .Values.global.namespace }} + labels: + {{- include "resourcespace.labels" . | nindent 4 }} + {{- include "resourcespace.selectorLabels" . | nindent 4 }} +spec: + type: ClusterIP + selector: + {{- include "resourcespace.selectorLabels" . | nindent 4 }} + ports: + - name: http + port: 80 + targetPort: http \ No newline at end of file diff --git a/helm/templates/mariadb.yaml b/helm/templates/mariadb.yaml new file mode 100755 index 0000000..bc8131c --- /dev/null +++ b/helm/templates/mariadb.yaml @@ -0,0 +1,129 @@ +# ============================================================================= +# MariaDB StatefulSet +# ============================================================================= +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "resourcespace.fullname" . }}-mariadb + namespace: {{ .Values.global.namespace }} + labels: + {{- include "resourcespace.labels" . | nindent 4 }} + {{- include "mariadb.selectorLabels" . | nindent 4 }} +spec: + serviceName: {{ include "mariadb.serviceName" . }} + replicas: 1 # MariaDB single-instance; change to 3+ with Galera for HA + selector: + matchLabels: + {{- include "mariadb.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "resourcespace.labels" . | nindent 8 }} + {{- include "mariadb.selectorLabels" . | nindent 8 }} + spec: + # No securityContext set — OpenShift assigns a UID from the namespace + # range (1001020000–1001029999). MariaDB 11 runs fine as an arbitrary UID. + containers: + - name: mariadb + image: "{{ .Values.mariadb.image.repository }}:{{ .Values.mariadb.image.tag }}" + imagePullPolicy: {{ .Values.mariadb.image.pullPolicy }} + ports: + - name: mysql + containerPort: 3306 + protocol: TCP + envFrom: + - secretRef: + name: {{ include "mariadb.secretName" . }} + volumeMounts: + - name: mariadb-data + mountPath: /var/lib/mysql + resources: + {{- toYaml .Values.mariadb.resources | nindent 12 }} + + volumes: + - name: mariadb-data + persistentVolumeClaim: + claimName: {{ include "resourcespace.fullname" . }}-mariadb-pvc + + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + +--- +# ============================================================================= +# MariaDB PersistentVolumeClaim +# Declared as a standalone PVC (not a volumeClaimTemplate) so it persists +# across StatefulSet re-creations +# ============================================================================= +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "resourcespace.fullname" . }}-mariadb-pvc + namespace: {{ .Values.global.namespace }} + labels: + {{- include "resourcespace.labels" . | nindent 4 }} + annotations: + helm.sh/resource-policy: keep +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.mariadb.persistence.size }} + {{- if .Values.persistence.storageClassName }} + storageClassName: {{ .Values.persistence.storageClassName | quote }} + {{- end }} + +--- +# ============================================================================= +# MariaDB headless Service +# Provides stable DNS: ...svc.cluster.local +# ResourceSpace connects to the ClusterIP service below, not this one. +# ============================================================================= +apiVersion: v1 +kind: Service +metadata: + name: {{ include "mariadb.serviceName" . }}-headless + namespace: {{ .Values.global.namespace }} + labels: + {{- include "resourcespace.labels" . | nindent 4 }} + {{- include "mariadb.selectorLabels" . | nindent 4 }} +spec: + clusterIP: None + selector: + {{- include "mariadb.selectorLabels" . | nindent 4 }} + ports: + - name: mysql + port: 3306 + targetPort: mysql + +--- +# ============================================================================= +# MariaDB ClusterIP Service +# This is the hostname ResourceSpace uses: -mariadb..svc +# ============================================================================= +apiVersion: v1 +kind: Service +metadata: + name: {{ include "mariadb.serviceName" . }} + namespace: {{ .Values.global.namespace }} + labels: + {{- include "resourcespace.labels" . | nindent 4 }} + {{- include "mariadb.selectorLabels" . | nindent 4 }} +spec: + type: ClusterIP + selector: + {{- include "mariadb.selectorLabels" . | nindent 4 }} + ports: + - name: mysql + port: 3306 + targetPort: mysql \ No newline at end of file diff --git a/helm/templates/pvc-filestore.yaml b/helm/templates/pvc-filestore.yaml new file mode 100755 index 0000000..ea83e1e --- /dev/null +++ b/helm/templates/pvc-filestore.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "resourcespace.fullname" . }}-filestore-pvc + namespace: {{ .Values.global.namespace }} + labels: + {{- include "resourcespace.labels" . | nindent 4 }} + annotations: + # Prevents accidental deletion of the PVC on helm uninstall. + # Delete manually if you are sure you no longer need the data. + helm.sh/resource-policy: keep +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.persistence.filestore.size }} + {{- if .Values.persistence.storageClassName }} + storageClassName: {{ .Values.persistence.storageClassName | quote }} + {{- end }} \ No newline at end of file diff --git a/helm/templates/route.yaml b/helm/templates/route.yaml new file mode 100755 index 0000000..2a1e23b --- /dev/null +++ b/helm/templates/route.yaml @@ -0,0 +1,37 @@ +# ============================================================================= +# OpenShift Route +# +# Exposes ResourceSpace via the cluster's built-in HAProxy router. +# TLS is terminated at the edge (router); traffic inside the cluster is HTTP. +# +# To switch to a LoadBalancer Service later: +# 1. Delete or disable this Route. +# 2. Change the Service type in deployment.yaml to LoadBalancer. +# 3. Add any cloud/on-prem load balancer annotations to the Service. +# ============================================================================= +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: {{ include "resourcespace.fullname" . }} + namespace: {{ .Values.global.namespace }} + labels: + {{- include "resourcespace.labels" . | nindent 4 }} + {{- include "resourcespace.selectorLabels" . | nindent 4 }} +spec: + host: {{ .Values.resourcespace.hostname | quote }} + to: + kind: Service + name: {{ include "resourcespace.fullname" . }} + weight: 100 + port: + targetPort: http + tls: + termination: {{ .Values.resourcespace.tls.termination }} + insecureEdgeTerminationPolicy: {{ .Values.resourcespace.tls.insecureEdgeTerminationPolicy }} + {{- if .Values.resourcespace.tls.secretName }} + # Custom TLS certificate loaded from a Secret. + # The Secret must exist in the same namespace and contain tls.crt / tls.key. + externalCertificate: + name: {{ .Values.resourcespace.tls.secretName }} + {{- end }} + wildcardPolicy: None \ No newline at end of file diff --git a/helm/templates/secret.yaml b/helm/templates/secret.yaml new file mode 100755 index 0000000..e135796 --- /dev/null +++ b/helm/templates/secret.yaml @@ -0,0 +1,27 @@ +{{/* + Secret holding MariaDB credentials. + + PRODUCTION NOTE: Do not store real passwords in values.yaml committed to git. + Use one of: + - Sealed Secrets (https://github.com/bitnami-labs/sealed-secrets) + - External Secrets Operator + Vault / AWS SSM + - `helm install --set mariadb.auth.password=` at deploy time + + To use a pre-existing external secret instead of this generated one, + delete or skip this file and update the env references in the Deployment + and StatefulSet to point at your own Secret name/keys. +*/}} +{{- include "resourcespace.validateValues" . }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "mariadb.secretName" . }} + namespace: {{ .Values.global.namespace }} + labels: + {{- include "resourcespace.labels" . | nindent 4 }} +type: Opaque +stringData: + MARIADB_ROOT_PASSWORD: {{ .Values.mariadb.auth.rootPassword | quote }} + MARIADB_DATABASE: {{ .Values.mariadb.auth.database | quote }} + MARIADB_USER: {{ .Values.mariadb.auth.username | quote }} + MARIADB_PASSWORD: {{ .Values.mariadb.auth.password | quote }} \ No newline at end of file diff --git a/helm/values.yaml b/helm/values.yaml new file mode 100755 index 0000000..90dd729 --- /dev/null +++ b/helm/values.yaml @@ -0,0 +1,108 @@ +# ============================================================================= +# ResourceSpace Helm Chart - values.yaml +# ============================================================================= +# Override any value with: helm install resourcespace . -f my-overrides.yaml +# or with: --set key=value on the command line. + +# ----------------------------------------------------------------------------- +# Global +# ----------------------------------------------------------------------------- +global: + # Kubernetes namespace to deploy into (must already exist, or use + # `oc new-project ` before installing). + namespace: + + +# ----------------------------------------------------------------------------- +# ResourceSpace application +# ----------------------------------------------------------------------------- +resourcespace: + image: + # Use the official image from Docker Hub, or your internal registry. + repository: + tag: + pullPolicy: Always + + + # Number of ResourceSpace web pods. + # NOTE: ResourceSpace writes files to the filestore volume - keep replicas at + # 1 unless you have configured shared ReadWriteMany NFS storage and tested + # concurrent access carefully. + replicaCount: 1 + + # The publicly reachable hostname for the OpenShift Route. + # CONFIRM: set this to your actual cluster ingress domain. + # Example: resourcespace.apps.mycluster.example.com + hostname: + + # TLS for the OpenShift Route. + tls: + # edge = OpenShift terminates TLS; insecureEdgeTerminationPolicy redirects HTTP→HTTPS. + termination: edge + insecureEdgeTerminationPolicy: Redirect + # Leave secretName empty to use the cluster's default wildcard certificate, + # or set it to the name of a TLS Secret in the same namespace. + secretName: "" + + # Resource requests/limits for the ResourceSpace pod. + resources: + requests: + cpu: "100m" + memory: "256Mi" + limits: + cpu: "250m" + memory: "512Mi" + + # Additional environment variables injected into the ResourceSpace pod. + extraEnv: [] + +# ----------------------------------------------------------------------------- +# MariaDB (in-cluster StatefulSet) +# ----------------------------------------------------------------------------- +mariadb: + image: + repository: mariadb + tag: "11" + pullPolicy: IfNotPresent + + # Database credentials. + # SECURITY: move these to an external secret manager (Vault, SealedSecrets) + # before running in production. The chart creates a Kubernetes Secret from + # these values; never commit real passwords to source control. + auth: + rootPassword: "" # Required — set with --set mariadb.auth.rootPassword= + database: "resourcespace" + username: "resourcespace" + password: "" # Required — set with --set mariadb.auth.password= + + resources: + requests: + cpu: "100m" + memory: "256Mi" + limits: + cpu: "250m" + memory: "512Mi" + + # Persistent volume for MariaDB data directory (/var/lib/mysql). + persistence: + size: 20Gi + +# ----------------------------------------------------------------------------- +# Persistence +# ----------------------------------------------------------------------------- +persistence: + # storageClassName applies to both the filestore and MariaDB PVCs when using + # dynamic provisioning. Leave as "" to use the cluster's default StorageClass. + # Run `oc get storageclass` to see what is available on your cluster. + storageClassName: "" + + filestore: + size: 10Gi + + +# ----------------------------------------------------------------------------- +# Pod scheduling +# ----------------------------------------------------------------------------- +nodeSelector: {} +tolerations: [] +affinity: {} \ No newline at end of file diff --git a/ports.conf b/ports.conf new file mode 100644 index 0000000..ca35c5c --- /dev/null +++ b/ports.conf @@ -0,0 +1,5 @@ +# OpenShift-compatible Apache port config. +# Replaces /etc/apache2/ports.conf in the image build. +# Port 8080 is used instead of 80 because OpenShift's restricted-v2 SCC +# does not allow binding to ports below 1024. +Listen 8080 \ No newline at end of file From 5d9cc3f600ff7ed5668ab63172d84291fb087060 Mon Sep 17 00:00:00 2001 From: dap012 Date: Mon, 20 Apr 2026 12:43:26 -0600 Subject: [PATCH 3/3] Move files to avoid conflict with upstream --- 000-default.conf => openshift/000-default.conf | 0 Dockerfile => openshift/Dockerfile | 0 README.md => openshift/README.md | 0 entrypoint.sh => openshift/entrypoint.sh | 0 ports.conf => openshift/ports.conf | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename 000-default.conf => openshift/000-default.conf (100%) rename Dockerfile => openshift/Dockerfile (100%) rename README.md => openshift/README.md (100%) rename entrypoint.sh => openshift/entrypoint.sh (100%) rename ports.conf => openshift/ports.conf (100%) diff --git a/000-default.conf b/openshift/000-default.conf similarity index 100% rename from 000-default.conf rename to openshift/000-default.conf diff --git a/Dockerfile b/openshift/Dockerfile similarity index 100% rename from Dockerfile rename to openshift/Dockerfile diff --git a/README.md b/openshift/README.md similarity index 100% rename from README.md rename to openshift/README.md diff --git a/entrypoint.sh b/openshift/entrypoint.sh similarity index 100% rename from entrypoint.sh rename to openshift/entrypoint.sh diff --git a/ports.conf b/openshift/ports.conf similarity index 100% rename from ports.conf rename to openshift/ports.conf