diff --git a/indico/app.yaml b/indico/app.yaml new file mode 100644 index 0000000..86ccf3b --- /dev/null +++ b/indico/app.yaml @@ -0,0 +1,6 @@ +name: indico +is: indico +description: Indico is an open-source conference and event management system developed at CERN. It handles the full lifecycle of events from abstract submission to proceedings publication. +icon: https://raw.githubusercontent.com/indico/indico/master/indico/web/static/images/logo_indico.png +category: productivity +latest: "3" diff --git a/indico/versions/3/README.md b/indico/versions/3/README.md new file mode 100644 index 0000000..dd8b2ea --- /dev/null +++ b/indico/versions/3/README.md @@ -0,0 +1,43 @@ +# Indico + +Indico is a feature-rich open-source conference and event management system, developed by CERN. + +## Dependencies + +- **PostgreSQL** - Database for storing events, registrations, and abstracts +- **Redis** - Used for caching and background job queuing +- **SMTP** - For sending registration confirmations and notifications + +## Configuration + +Key settings in `config.yaml`: + +- **domain** - Where Indico will be accessible +- **storage** - Persistent volume size for uploaded files (default: `2Gi`) +- **timezone** - Default timezone for events (default: `UTC`) +- **smtp** - Email settings inherited from your Wild Cloud SMTP service + +## First-Time Setup + +1. Add and deploy the app: + ```bash + wild app add indico + wild app deploy indico + ``` + +2. Create the admin user via the Indico shell: + ```bash + kubectl exec -n indico deploy/indico -- indico shell -c \ + "from indico.modules.users.operations import create_user; \ + from indico.core.db import db; \ + u = create_user('admin@example.com', {'first_name': 'Admin', 'last_name': 'User', 'affiliation': ''}, is_admin=True); \ + db.session.commit(); print('Done')" + ``` + +3. Log in at the app URL with the admin account and set a password via **Profile → Change Password**. + +## Notes + +- Indico is built for scientific and academic conferences — it supports abstracts, paper reviews, registration forms, and room booking +- The `secretKey` is auto-generated in `secrets.yaml` +- Categories are the top-level containers; events and conferences belong to categories diff --git a/indico/versions/3/configmap.yaml b/indico/versions/3/configmap.yaml new file mode 100644 index 0000000..67d64ab --- /dev/null +++ b/indico/versions/3/configmap.yaml @@ -0,0 +1,44 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: indico-config + namespace: {{ .namespace }} +data: + indico.conf.tpl: | + import os + + # Database - credentials injected via environment variables + SQLALCHEMY_DATABASE_URI = f'postgresql://{os.environ["PGUSER"]}:{os.environ["PGPASSWORD"]}@{os.environ["PGHOST"]}:{os.environ["PGPORT"]}/{os.environ["PGDATABASE"]}' + del os + + SECRET_KEY = 'SECRET_KEY_PLACEHOLDER' + + BASE_URL = 'https://{{ .domain }}' + USE_PROXY = True + + DEFAULT_TIMEZONE = '{{ .timezone }}' + DEFAULT_LOCALE = 'en_GB' + + REDIS_CACHE_URL = 'redis://:REDIS_PASSWORD_PLACEHOLDER@{{ .redis.host }}:{{ .redis.port }}/0' + CELERY_BROKER = 'redis://:REDIS_PASSWORD_PLACEHOLDER@{{ .redis.host }}:{{ .redis.port }}/1' + + NO_REPLY_EMAIL = '{{ .smtp.from }}' + SUPPORT_EMAIL = '{{ .smtp.from }}' + + SMTP_SERVER = '{{ .smtp.host }}' + SMTP_PORT = {{ .smtp.port }} + SMTP_USE_TLS = False + SMTP_LOGIN = '{{ .smtp.user }}' + SMTP_PASSWORD = 'SMTP_PASSWORD_PLACEHOLDER' + + LOG_DIR = '/opt/indico/log' + TEMP_DIR = '/opt/indico/tmp' + CACHE_DIR = '/opt/indico/cache' + CUSTOMIZATION_DIR = '/opt/indico/custom' + + STORAGE_BACKENDS = {'default': 'fs:/opt/indico/archive'} + ATTACHMENT_STORAGE = 'default' + + ENABLE_ROOMBOOKING = True + + PLUGINS = {'previewer_code'} diff --git a/indico/versions/3/db-init-job.yaml b/indico/versions/3/db-init-job.yaml new file mode 100644 index 0000000..86b22bc --- /dev/null +++ b/indico/versions/3/db-init-job.yaml @@ -0,0 +1,68 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: indico-db-init + namespace: {{ .namespace }} + labels: + component: db-init +spec: + template: + metadata: + labels: + component: db-init + spec: + restartPolicy: OnFailure + securityContext: + runAsNonRoot: true + runAsUser: 999 + runAsGroup: 999 + seccompProfile: + type: RuntimeDefault + containers: + - name: db-init + image: postgres:17 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: false + command: ["/bin/bash", "-c"] + args: + - | + PGPASSWORD=${POSTGRES_ADMIN_PASSWORD} psql -h ${DB_HOST} -U postgres < /opt/indico/etc/indico.conf + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: false + env: + - name: SECRET_KEY + valueFrom: + secretKeyRef: + name: indico-secrets + key: secretKey + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: indico-secrets + key: redis.password + - name: SMTP_PASSWORD + valueFrom: + secretKeyRef: + name: indico-secrets + key: smtp.password + volumeMounts: + - name: indico-config-base + mountPath: /etc/indico-base + - name: indico-config + mountPath: /opt/indico/etc + containers: + - name: worker + image: getindico/indico:3.3.12 + args: ["/opt/indico/run_celery.sh"] + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: false + resources: + requests: + memory: 256Mi + cpu: 50m + limits: + memory: 2Gi + cpu: "1" + env: + - name: PGHOST + value: "{{ .db.host }}" + - name: PGPORT + value: "{{ .db.port }}" + - name: PGUSER + value: "{{ .db.user }}" + - name: PGDATABASE + value: "{{ .db.name }}" + - name: PGPASSWORD + valueFrom: + secretKeyRef: + name: indico-secrets + key: dbPassword + volumeMounts: + - name: indico-config + mountPath: /opt/indico/etc + - name: indico-archive + mountPath: /opt/indico/archive + - name: indico-tmp + mountPath: /opt/indico/tmp + - name: indico-log + mountPath: /opt/indico/log + - name: indico-cache + mountPath: /opt/indico/cache + - name: indico-custom + mountPath: /opt/indico/custom + - name: beat + image: getindico/indico:3.3.12 + args: ["/opt/indico/run_celery.sh", "beat"] + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: false + resources: + requests: + memory: 256Mi + cpu: 50m + limits: + memory: 1Gi + cpu: 200m + env: + - name: PGHOST + value: "{{ .db.host }}" + - name: PGPORT + value: "{{ .db.port }}" + - name: PGUSER + value: "{{ .db.user }}" + - name: PGDATABASE + value: "{{ .db.name }}" + - name: PGPASSWORD + valueFrom: + secretKeyRef: + name: indico-secrets + key: dbPassword + volumeMounts: + - name: indico-config + mountPath: /opt/indico/etc + - name: indico-tmp + mountPath: /opt/indico/tmp + - name: indico-log + mountPath: /opt/indico/log + - name: indico-cache + mountPath: /opt/indico/cache + volumes: + - name: indico-config-base + configMap: + name: indico-config + - name: indico-config + emptyDir: {} + - name: indico-archive + persistentVolumeClaim: + claimName: indico-archive + - name: indico-tmp + emptyDir: + medium: Memory + - name: indico-log + emptyDir: {} + - name: indico-cache + emptyDir: {} + - name: indico-custom + emptyDir: {} diff --git a/indico/versions/3/deployment.yaml b/indico/versions/3/deployment.yaml new file mode 100644 index 0000000..8108f42 --- /dev/null +++ b/indico/versions/3/deployment.yaml @@ -0,0 +1,142 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: indico + namespace: {{ .namespace }} +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + component: web + template: + metadata: + labels: + component: web + spec: + securityContext: + runAsNonRoot: true + runAsUser: 999 + runAsGroup: 999 + fsGroup: 999 + seccompProfile: + type: RuntimeDefault + initContainers: + - name: config-init + image: getindico/indico:3.3.12 + command: ["/bin/sh", "-c"] + args: + - | + sed \ + -e "s|SECRET_KEY_PLACEHOLDER|${SECRET_KEY}|g" \ + -e "s|REDIS_PASSWORD_PLACEHOLDER|${REDIS_PASSWORD}|g" \ + -e "s|SMTP_PASSWORD_PLACEHOLDER|${SMTP_PASSWORD}|g" \ + /etc/indico-base/indico.conf.tpl > /opt/indico/etc/indico.conf + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: false + env: + - name: SECRET_KEY + valueFrom: + secretKeyRef: + name: indico-secrets + key: secretKey + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: indico-secrets + key: redis.password + - name: SMTP_PASSWORD + valueFrom: + secretKeyRef: + name: indico-secrets + key: smtp.password + volumeMounts: + - name: indico-config-base + mountPath: /etc/indico-base + - name: indico-config + mountPath: /opt/indico/etc + containers: + - name: indico + image: getindico/indico:3.3.12 + args: ["/opt/indico/run_indico.sh"] + ports: + - name: http + containerPort: 59999 + protocol: TCP + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: false + resources: + requests: + memory: 256Mi + cpu: 50m + limits: + memory: 2Gi + cpu: "2" + env: + - name: PGHOST + value: "{{ .db.host }}" + - name: PGPORT + value: "{{ .db.port }}" + - name: PGUSER + value: "{{ .db.user }}" + - name: PGDATABASE + value: "{{ .db.name }}" + - name: PGPASSWORD + valueFrom: + secretKeyRef: + name: indico-secrets + key: dbPassword + livenessProbe: + tcpSocket: + port: 59999 + initialDelaySeconds: 120 + periodSeconds: 30 + failureThreshold: 6 + readinessProbe: + tcpSocket: + port: 59999 + initialDelaySeconds: 60 + periodSeconds: 15 + failureThreshold: 3 + volumeMounts: + - name: indico-config + mountPath: /opt/indico/etc + - name: indico-archive + mountPath: /opt/indico/archive + - name: indico-tmp + mountPath: /opt/indico/tmp + - name: indico-log + mountPath: /opt/indico/log + - name: indico-cache + mountPath: /opt/indico/cache + - name: indico-custom + mountPath: /opt/indico/custom + - name: indico-static + mountPath: /opt/indico/static-shared + volumes: + - name: indico-config-base + configMap: + name: indico-config + - name: indico-config + emptyDir: {} + - name: indico-archive + persistentVolumeClaim: + claimName: indico-archive + - name: indico-tmp + emptyDir: + medium: Memory + - name: indico-log + emptyDir: {} + - name: indico-cache + emptyDir: {} + - name: indico-custom + emptyDir: {} + - name: indico-static + emptyDir: {} diff --git a/indico/versions/3/ingress.yaml b/indico/versions/3/ingress.yaml new file mode 100644 index 0000000..0823acd --- /dev/null +++ b/indico/versions/3/ingress.yaml @@ -0,0 +1,26 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: indico + namespace: {{ .namespace }} + annotations: + external-dns.alpha.kubernetes.io/target: {{ .externalDnsDomain }} + external-dns.alpha.kubernetes.io/cloudflare-proxied: "false" + external-dns.alpha.kubernetes.io/ttl: "60" +spec: + ingressClassName: traefik + rules: + - host: {{ .domain }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: indico + port: + number: 80 + tls: + - hosts: + - {{ .domain }} + secretName: {{ .tlsSecretName }} diff --git a/indico/versions/3/kustomization.yaml b/indico/versions/3/kustomization.yaml new file mode 100644 index 0000000..46906a4 --- /dev/null +++ b/indico/versions/3/kustomization.yaml @@ -0,0 +1,18 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: {{ .namespace }} +labels: + - includeSelectors: true + pairs: + app: indico + managedBy: kustomize + partOf: wild-cloud +resources: + - namespace.yaml + - configmap.yaml + - pvc.yaml + - db-init-job.yaml + - deployment.yaml + - deployment-worker.yaml + - service.yaml + - ingress.yaml diff --git a/indico/versions/3/manifest.yaml b/indico/versions/3/manifest.yaml new file mode 100644 index 0000000..799b882 --- /dev/null +++ b/indico/versions/3/manifest.yaml @@ -0,0 +1,32 @@ +version: 3.3.12-1 +requires: + - name: postgres + - name: redis + - name: smtp +defaultConfig: + namespace: indico + externalDnsDomain: "{{ .cloud.domain }}" + domain: "indico.{{ .cloud.domain }}" + tlsSecretName: wildcard-wild-cloud-tls + storage: 2Gi + timezone: UTC + db: + host: "{{ .apps.postgres.host }}" + port: "{{ .apps.postgres.port }}" + name: indico + user: indico + redis: + host: "{{ .apps.redis.host }}" + port: "6379" + smtp: + host: "{{ .apps.smtp.host }}" + port: "{{ .apps.smtp.port }}" + from: "{{ .apps.smtp.from }}" + user: "{{ .apps.smtp.user }}" +defaultSecrets: + - key: dbPassword + - key: secretKey +requiredSecrets: + - postgres.password + - redis.password + - smtp.password diff --git a/indico/versions/3/namespace.yaml b/indico/versions/3/namespace.yaml new file mode 100644 index 0000000..054927e --- /dev/null +++ b/indico/versions/3/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: {{ .namespace }} diff --git a/indico/versions/3/pvc.yaml b/indico/versions/3/pvc.yaml new file mode 100644 index 0000000..b92a019 --- /dev/null +++ b/indico/versions/3/pvc.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: indico-archive + namespace: {{ .namespace }} +spec: + accessModes: + - ReadWriteMany + storageClassName: nfs + resources: + requests: + storage: {{ .storage }} diff --git a/indico/versions/3/service.yaml b/indico/versions/3/service.yaml new file mode 100644 index 0000000..a22b156 --- /dev/null +++ b/indico/versions/3/service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: indico + namespace: {{ .namespace }} +spec: + selector: + component: web + ports: + - name: http + port: 80 + targetPort: 59999 + protocol: TCP