From 0ba33a315d6f69b1c5d4ffd95acf83c96a2d7d17 Mon Sep 17 00:00:00 2001 From: Paul Payne Date: Sun, 4 Jan 2026 19:36:15 +0000 Subject: [PATCH] Add Decidim. --- decidim/Dockerfile | 25 +++++ decidim/db-init-job.yaml | 73 ++++++++++++ decidim/deployment.yaml | 214 ++++++++++++++++++++++++++++++++++++ decidim/ingress.yaml | 26 +++++ decidim/kustomization.yaml | 17 +++ decidim/manifest.yaml | 44 ++++++++ decidim/namespace.yaml | 4 + decidim/pvc.yaml | 12 ++ decidim/service.yaml | 15 +++ decidim/serviceaccount.yaml | 7 ++ 10 files changed, 437 insertions(+) create mode 100644 decidim/Dockerfile create mode 100644 decidim/db-init-job.yaml create mode 100644 decidim/deployment.yaml create mode 100644 decidim/ingress.yaml create mode 100644 decidim/kustomization.yaml create mode 100644 decidim/manifest.yaml create mode 100644 decidim/namespace.yaml create mode 100644 decidim/pvc.yaml create mode 100644 decidim/service.yaml create mode 100644 decidim/serviceaccount.yaml diff --git a/decidim/Dockerfile b/decidim/Dockerfile new file mode 100644 index 0000000..add7fee --- /dev/null +++ b/decidim/Dockerfile @@ -0,0 +1,25 @@ +# Build Decidim with Sidekiq support +FROM decidim/decidim:0.31.0 + +# Switch to root to install dependencies +USER root + +# Add sidekiq to Gemfile +RUN cd /code && \ + echo "" >> Gemfile && \ + echo "# Background job processing" >> Gemfile && \ + echo "gem 'sidekiq', '~> 6.5'" >> Gemfile && \ + bundle install + +# Configure Rails to use Sidekiq as ActiveJob backend +RUN cd /code && \ + test -d config/initializers || mkdir -p config/initializers && \ + echo "Rails.application.config.active_job.queue_adapter = :sidekiq" > config/initializers/active_job.rb && \ + cat config/initializers/active_job.rb && \ + ls -la config/initializers/ + +# Switch back to decidim user +USER decidim + +# Default command (can be overridden) +CMD ["bundle", "exec", "rails", "s", "-b", "0.0.0.0"] diff --git a/decidim/db-init-job.yaml b/decidim/db-init-job.yaml new file mode 100644 index 0000000..fbe71e3 --- /dev/null +++ b/decidim/db-init-job.yaml @@ -0,0 +1,73 @@ +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: decidim-db-init + namespace: decidim +spec: + ttlSecondsAfterFinished: 300 + template: + metadata: + labels: + component: db-init + spec: + restartPolicy: OnFailure + securityContext: + runAsNonRoot: true + runAsUser: 999 + runAsGroup: 999 + fsGroup: 999 + seccompProfile: + type: RuntimeDefault + containers: + - name: db-init + image: postgres:17 + imagePullPolicy: IfNotPresent + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + command: + - /bin/bash + - -c + - | + set -e + export PGPASSWORD="${POSTGRES_ADMIN_PASSWORD}" + + # Create database if it doesn't exist + psql -h "${POSTGRES_HOST}" -U "${POSTGRES_ADMIN_USER}" -d postgres -tc "SELECT 1 FROM pg_database WHERE datname = '${DB_NAME}'" | grep -q 1 || \ + psql -h "${POSTGRES_HOST}" -U "${POSTGRES_ADMIN_USER}" -d postgres -c "CREATE DATABASE ${DB_NAME};" + + # Create user if it doesn't exist, or update password if it does + psql -h "${POSTGRES_HOST}" -U "${POSTGRES_ADMIN_USER}" -d postgres -tc "SELECT 1 FROM pg_roles WHERE rolname = '${DB_USER}'" | grep -q 1 && \ + psql -h "${POSTGRES_HOST}" -U "${POSTGRES_ADMIN_USER}" -d postgres -c "ALTER USER ${DB_USER} WITH PASSWORD '${DB_PASSWORD}';" || \ + psql -h "${POSTGRES_HOST}" -U "${POSTGRES_ADMIN_USER}" -d postgres -c "CREATE USER ${DB_USER} WITH PASSWORD '${DB_PASSWORD}';" + + # Grant privileges + psql -h "${POSTGRES_HOST}" -U "${POSTGRES_ADMIN_USER}" -d postgres -c "GRANT ALL PRIVILEGES ON DATABASE ${DB_NAME} TO ${DB_USER};" + + # Grant schema privileges (needed for Rails migrations) + psql -h "${POSTGRES_HOST}" -U "${POSTGRES_ADMIN_USER}" -d "${DB_NAME}" -c "GRANT ALL ON SCHEMA public TO ${DB_USER};" + + echo "Database initialization completed successfully" + env: + - name: POSTGRES_HOST + value: {{ .dbHostname }} + - name: POSTGRES_ADMIN_USER + value: postgres + - name: POSTGRES_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: decidim-secrets + key: postgres.password + - name: DB_NAME + value: {{ .dbName }} + - name: DB_USER + value: {{ .dbUsername }} + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: decidim-secrets + key: dbPassword diff --git a/decidim/deployment.yaml b/decidim/deployment.yaml new file mode 100644 index 0000000..5e05a49 --- /dev/null +++ b/decidim/deployment.yaml @@ -0,0 +1,214 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: decidim + namespace: decidim +spec: + replicas: 1 + selector: + matchLabels: + component: web + strategy: + type: Recreate + template: + metadata: + labels: + component: web + spec: + automountServiceAccountToken: false + serviceAccountName: decidim + securityContext: + fsGroup: 1000 + fsGroupChangePolicy: Always + containers: + - name: decidim + image: payneio/decidim-sidekiq:0.31.0 + imagePullPolicy: Always + command: + - /bin/bash + - -c + - | + set -e + cd /code + bundle exec rake db:migrate + bundle exec rails runner "Decidim::System::Admin.find_or_create_by!(email: ENV['SYSTEM_ADMIN_EMAIL']) { |admin| admin.password = ENV['SYSTEM_ADMIN_PASSWORD']; admin.password_confirmation = ENV['SYSTEM_ADMIN_PASSWORD'] }" + bundle exec rails s -b 0.0.0.0 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + add: + - CHOWN + - FOWNER + - SETGID + - SETUID + - DAC_OVERRIDE + privileged: false + readOnlyRootFilesystem: false + runAsNonRoot: false + runAsUser: 0 + seccompProfile: + type: RuntimeDefault + env: + - name: RAILS_ENV + value: "production" + - name: PORT + value: "{{ .port }}" + - name: RAILS_LOG_TO_STDOUT + value: "true" + # Database configuration + - name: DATABASE_URL + valueFrom: + secretKeyRef: + name: decidim-secrets + key: dbUrl + # Redis configuration + - name: REDIS_HOSTNAME + value: {{ .redisHostname }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: decidim-secrets + key: redis.password + - name: REDIS_URL + value: "redis://:$(REDIS_PASSWORD)@$(REDIS_HOSTNAME):6379/0" + # Application configuration + - name: DECIDIM_HOST + value: {{ .domain }} + - name: DECIDIM_ORGANIZATION_NAME + value: {{ .siteName }} + - name: SECRET_KEY_BASE + valueFrom: + secretKeyRef: + name: decidim-secrets + key: secretKeyBase + # SMTP configuration + - name: SMTP_ADDRESS + value: {{ .smtp.host }} + - name: SMTP_PORT + value: "{{ .smtp.port }}" + - name: SMTP_USERNAME + value: {{ .smtp.user }} + - name: SMTP_PASSWORD + valueFrom: + secretKeyRef: + name: decidim-secrets + key: smtpPassword + - name: SMTP_DOMAIN + value: {{ .domain }} + - name: SMTP_FROM + value: {{ .smtp.from }} + - name: SMTP_STARTTLS_AUTO + value: "{{ .smtp.startTls }}" + # System admin credentials + - name: SYSTEM_ADMIN_EMAIL + value: {{ .systemAdminEmail }} + - name: SYSTEM_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: decidim-secrets + key: systemAdminPassword + ports: + - name: http + containerPort: {{ .port }} + protocol: TCP + livenessProbe: + tcpSocket: + port: {{ .port }} + initialDelaySeconds: 300 + periodSeconds: 30 + timeoutSeconds: 10 + successThreshold: 1 + failureThreshold: 6 + readinessProbe: + tcpSocket: + port: {{ .port }} + initialDelaySeconds: 180 + periodSeconds: 30 + timeoutSeconds: 10 + successThreshold: 1 + failureThreshold: 6 + resources: + limits: + cpu: 2000m + ephemeral-storage: 10Gi + memory: 4Gi + requests: + cpu: 500m + ephemeral-storage: 50Mi + memory: 1Gi + volumeMounts: + - name: decidim-data + mountPath: /code/public/uploads + - name: sidekiq + image: payneio/decidim-sidekiq:0.31.0 + imagePullPolicy: Always + command: + - /bin/bash + - -c + - | + set -e + cd /code + bundle exec sidekiq + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + add: + - CHOWN + - FOWNER + - SETGID + - SETUID + - DAC_OVERRIDE + privileged: false + readOnlyRootFilesystem: false + runAsNonRoot: false + runAsUser: 0 + seccompProfile: + type: RuntimeDefault + env: + - name: RAILS_ENV + value: "production" + - name: RAILS_LOG_TO_STDOUT + value: "true" + # Database configuration + - name: DATABASE_URL + valueFrom: + secretKeyRef: + name: decidim-secrets + key: dbUrl + # Redis configuration + - name: REDIS_HOSTNAME + value: {{ .redisHostname }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: decidim-secrets + key: redis.password + - name: REDIS_URL + value: "redis://:$(REDIS_PASSWORD)@$(REDIS_HOSTNAME):6379/0" + # Application configuration + - name: DECIDIM_HOST + value: {{ .domain }} + - name: SECRET_KEY_BASE + valueFrom: + secretKeyRef: + name: decidim-secrets + key: secretKeyBase + resources: + limits: + cpu: 1000m + memory: 2Gi + requests: + cpu: 250m + memory: 512Mi + volumeMounts: + - name: decidim-data + mountPath: /code/public/uploads + volumes: + - name: decidim-data + persistentVolumeClaim: + claimName: decidim-data diff --git a/decidim/ingress.yaml b/decidim/ingress.yaml new file mode 100644 index 0000000..0686918 --- /dev/null +++ b/decidim/ingress.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: decidim + namespace: decidim + annotations: + external-dns.alpha.kubernetes.io/target: {{ .externalDnsDomain }} + external-dns.alpha.kubernetes.io/cloudflare-proxied: "false" +spec: + ingressClassName: traefik + tls: + - hosts: + - {{ .domain }} + secretName: {{ .tlsSecretName }} + rules: + - host: {{ .domain }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: decidim + port: + number: {{ .port }} diff --git a/decidim/kustomization.yaml b/decidim/kustomization.yaml new file mode 100644 index 0000000..14c97e0 --- /dev/null +++ b/decidim/kustomization.yaml @@ -0,0 +1,17 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: decidim +labels: + - includeSelectors: true + pairs: + app: decidim + managedBy: kustomize + partOf: wild-cloud +resources: + - namespace.yaml + - serviceaccount.yaml + - pvc.yaml + - db-init-job.yaml + - deployment.yaml + - service.yaml + - ingress.yaml diff --git a/decidim/manifest.yaml b/decidim/manifest.yaml new file mode 100644 index 0000000..6b5178c --- /dev/null +++ b/decidim/manifest.yaml @@ -0,0 +1,44 @@ +name: decidim +is: decidim +description: Decidim is a participatory democracy framework for cities and organizations. Built in Ruby on Rails, it enables citizen participation through proposals, debates, and voting. Includes Sidekiq for background job processing. +version: 0.31.0 +icon: https://raw.githubusercontent.com/decidim/decidim/develop/logo.svg +requires: + - name: postgres + installed_as: postgres + - name: redis + installed_as: redis +defaultConfig: + namespace: decidim + externalDnsDomain: "{{ .cloud.domain }}" + timezone: UTC + port: 3000 + storage: 20Gi + systemAdminEmail: "{{ .operator.email }}" + siteName: "Decidim" + domain: decidim.{{ .cloud.domain }} + dbHostname: "{{ .apps.postgres.host }}" + dbPort: "{{ .apps.postgres.port }}" + dbUsername: decidim + dbName: decidim + redisHostname: "{{ .apps.redis.host }}" + tlsSecretName: wildcard-wild-cloud-tls + smtp: + enabled: true + host: "{{ .cloud.smtp.host }}" + port: "{{ .cloud.smtp.port }}" + user: "{{ .cloud.smtp.user }}" + from: "{{ .cloud.smtp.from }}" + tls: "{{ .cloud.smtp.tls }}" + startTls: "{{ .cloud.smtp.startTls }}" +defaultSecrets: + - key: systemAdminPassword + - key: secretKeyBase + default: "{{ random.AlphaNum 128 }}" + - key: smtpPassword + - key: dbPassword + - key: dbUrl + default: "postgres://{{ .app.dbUsername }}:{{ .secrets.dbPassword }}@{{ .app.dbHostname }}:{{ .app.dbPort }}/{{ .app.dbName }}" +requiredSecrets: + - postgres.password + - redis.password diff --git a/decidim/namespace.yaml b/decidim/namespace.yaml new file mode 100644 index 0000000..27932b6 --- /dev/null +++ b/decidim/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: decidim diff --git a/decidim/pvc.yaml b/decidim/pvc.yaml new file mode 100644 index 0000000..8b3a27c --- /dev/null +++ b/decidim/pvc.yaml @@ -0,0 +1,12 @@ +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: decidim-data + namespace: decidim +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .storage }} diff --git a/decidim/service.yaml b/decidim/service.yaml new file mode 100644 index 0000000..7b0eecc --- /dev/null +++ b/decidim/service.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: decidim + namespace: decidim +spec: + selector: + component: web + ports: + - name: http + port: {{ .port }} + targetPort: http + protocol: TCP + type: ClusterIP diff --git a/decidim/serviceaccount.yaml b/decidim/serviceaccount.yaml new file mode 100644 index 0000000..1958294 --- /dev/null +++ b/decidim/serviceaccount.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: decidim + namespace: decidim +automountServiceAccountToken: false