From 52847814e0af9e53e0caaa97473f5752d7e77161 Mon Sep 17 00:00:00 2001 From: timotheereausanofi Date: Fri, 20 Mar 2026 10:15:15 +0100 Subject: [PATCH] Add OneLab Helm chart, Argo CD Application, and GitOps values for k3s Made-with: Cursor --- .gitignore | 5 + app/backup.sh | 3 + app/configurations.yml | 154 ++++++++ app/docker-compose.yml | 319 +++++++++++++++++ app/nginx/onelab.conf | 337 ++++++++++++++++++ app/playbooks/backup.yml | 11 + app/playbooks/deploy.yml | 18 + app/playbooks/install-swarm.yml | 25 ++ app/playbooks/install.yml | 13 + app/playbooks/reconfigure.yml | 15 + app/playbooks/stop.yml | 12 + app/playbooks/tasks/backup-task.yml | 37 ++ .../tasks/check-offline-resources.yml | 7 + app/playbooks/tasks/check-requirements.yml | 71 ++++ app/playbooks/tasks/check-ssl-files.yml | 49 +++ app/playbooks/tasks/init-docker.yml | 21 ++ app/playbooks/tasks/init-onelab.yml | 119 +++++++ app/playbooks/tasks/init-service.yml | 33 ++ app/playbooks/tasks/install-task.yml | 81 +++++ app/playbooks/tasks/manage-images.yml | 40 +++ app/playbooks/tasks/post-db-migration.yml | 28 ++ app/playbooks/tasks/post-update.yml | 21 ++ app/playbooks/tasks/pre-db-migration.yml | 14 + app/playbooks/tasks/pre-update.yml | 12 + app/playbooks/tasks/reconfigure-task.yml | 17 + .../tasks/rotate-ssl-files-for-rabbit.yml | 8 + app/playbooks/tasks/ssl-cert-migration.yml | 18 + app/playbooks/tasks/start-db-only.yml | 23 ++ app/playbooks/tasks/start-onelab.yml | 19 + app/playbooks/tasks/start-proxy.yml | 6 + app/playbooks/tasks/stop-db-only.yml | 6 + app/playbooks/tasks/stop-onelab.yml | 6 + app/playbooks/tasks/stop-proxy.yml | 6 + app/playbooks/tasks/update-conf-files.yml | 24 ++ app/playbooks/uninstall.yml | 53 +++ app/playbooks/update.yml | 115 ++++++ app/proxy/docker-compose.yml | 47 +++ app/proxy/error-404.html | 98 +++++ app/proxy/maintenance.conf | 9 + app/proxy/maintenance.html | 89 +++++ app/proxy/not-supported.html | 82 +++++ app/proxy/proxy.conf | 174 +++++++++ app/rabbit/advanced.conf | 5 + app/rabbit/definitions.json | 20 ++ app/rabbit/enable_plugins | 1 + app/rabbit/rabbit.conf | 54 +++ app/rabbit/ssl/rabbit.crt | 23 ++ app/rabbit/ssl/rabbit.fullchain.pem | 51 +++ app/rabbit/ssl/rabbit.key | 28 ++ app/reconfigure.sh | 21 ++ app/start.sh | 3 + app/stop.sh | 3 + app/uninstall.sh | 2 + app/update.sh | 26 ++ gitops/README.md | 52 +++ gitops/argocd/application.yaml | 26 ++ gitops/charts/onelab/Chart.yaml | 6 + gitops/charts/onelab/files/advanced.conf | 5 + .../charts/onelab/files/configurations.gotmpl | 91 +++++ gitops/charts/onelab/files/enable_plugins | 1 + gitops/charts/onelab/files/error-404.html | 98 +++++ gitops/charts/onelab/files/nginx.conf.tpl | 337 ++++++++++++++++++ .../onelab/files/rabbit-definitions.json | 20 ++ gitops/charts/onelab/files/rabbit.conf | 54 +++ gitops/charts/onelab/templates/_helpers.tpl | 25 ++ .../onelab/templates/configmap-rabbit.yaml | 18 + .../onelab/templates/configmap-revproxy.yaml | 14 + .../deployment-optional-workers.yaml | 111 ++++++ .../onelab/templates/deployment-redis.yaml | 53 +++ .../onelab/templates/deployment-revproxy.yaml | 73 ++++ .../templates/secret-configurations.yaml | 13 + .../onelab/templates/secret-postgres.yaml | 11 + .../onelab/templates/secret-rabbit-tls.yaml | 18 + .../charts/onelab/templates/service-db.yaml | 18 + .../onelab/templates/service-rabbitmq.yaml | 16 + .../templates/statefulset-postgres.yaml | 61 ++++ .../templates/statefulset-rabbitmq.yaml | 80 +++++ gitops/charts/onelab/templates/workloads.yaml | 116 ++++++ gitops/charts/onelab/values.yaml | 187 ++++++++++ gitops/values/k3s-example.yaml | 31 ++ install.sh | 16 + resources/restore/restore.sh | 3 + resources/restore/restore.yml | 104 ++++++ resources/scripts/centos/install_ansible.sh | 27 ++ resources/scripts/centos/install_docker.sh | 17 + resources/scripts/debian10/install_ansible.sh | 22 ++ resources/scripts/debian10/install_docker.sh | 26 ++ resources/scripts/debian11/install_ansible.sh | 13 + resources/scripts/debian11/install_docker.sh | 16 + resources/scripts/debian12/install_ansible.sh | 13 + resources/scripts/debian12/install_docker.sh | 16 + resources/scripts/fedora/install_ansible.sh | 19 + resources/scripts/fedora/install_docker.sh | 21 ++ resources/scripts/redhat7/install_ansible.sh | 20 ++ resources/scripts/redhat7/install_docker.sh | 27 ++ resources/scripts/redhat8/install_ansible.sh | 5 + resources/scripts/redhat8/install_docker.sh | 15 + resources/scripts/redhat9/install_ansible.sh | 5 + resources/scripts/redhat9/install_docker.sh | 15 + resources/scripts/ubuntu/install_ansible.sh | 22 ++ resources/scripts/ubuntu/install_docker.sh | 23 ++ resources/services/onelab.service | 14 + 102 files changed, 4476 insertions(+) create mode 100644 .gitignore create mode 100644 app/backup.sh create mode 100644 app/configurations.yml create mode 100644 app/docker-compose.yml create mode 100644 app/nginx/onelab.conf create mode 100644 app/playbooks/backup.yml create mode 100644 app/playbooks/deploy.yml create mode 100644 app/playbooks/install-swarm.yml create mode 100644 app/playbooks/install.yml create mode 100644 app/playbooks/reconfigure.yml create mode 100644 app/playbooks/stop.yml create mode 100644 app/playbooks/tasks/backup-task.yml create mode 100644 app/playbooks/tasks/check-offline-resources.yml create mode 100644 app/playbooks/tasks/check-requirements.yml create mode 100644 app/playbooks/tasks/check-ssl-files.yml create mode 100644 app/playbooks/tasks/init-docker.yml create mode 100644 app/playbooks/tasks/init-onelab.yml create mode 100644 app/playbooks/tasks/init-service.yml create mode 100644 app/playbooks/tasks/install-task.yml create mode 100644 app/playbooks/tasks/manage-images.yml create mode 100644 app/playbooks/tasks/post-db-migration.yml create mode 100644 app/playbooks/tasks/post-update.yml create mode 100644 app/playbooks/tasks/pre-db-migration.yml create mode 100644 app/playbooks/tasks/pre-update.yml create mode 100644 app/playbooks/tasks/reconfigure-task.yml create mode 100644 app/playbooks/tasks/rotate-ssl-files-for-rabbit.yml create mode 100644 app/playbooks/tasks/ssl-cert-migration.yml create mode 100644 app/playbooks/tasks/start-db-only.yml create mode 100644 app/playbooks/tasks/start-onelab.yml create mode 100644 app/playbooks/tasks/start-proxy.yml create mode 100644 app/playbooks/tasks/stop-db-only.yml create mode 100644 app/playbooks/tasks/stop-onelab.yml create mode 100644 app/playbooks/tasks/stop-proxy.yml create mode 100644 app/playbooks/tasks/update-conf-files.yml create mode 100644 app/playbooks/uninstall.yml create mode 100644 app/playbooks/update.yml create mode 100644 app/proxy/docker-compose.yml create mode 100644 app/proxy/error-404.html create mode 100644 app/proxy/maintenance.conf create mode 100644 app/proxy/maintenance.html create mode 100644 app/proxy/not-supported.html create mode 100644 app/proxy/proxy.conf create mode 100644 app/rabbit/advanced.conf create mode 100644 app/rabbit/definitions.json create mode 100644 app/rabbit/enable_plugins create mode 100644 app/rabbit/rabbit.conf create mode 100644 app/rabbit/ssl/rabbit.crt create mode 100644 app/rabbit/ssl/rabbit.fullchain.pem create mode 100644 app/rabbit/ssl/rabbit.key create mode 100644 app/reconfigure.sh create mode 100644 app/start.sh create mode 100644 app/stop.sh create mode 100644 app/uninstall.sh create mode 100644 app/update.sh create mode 100644 gitops/README.md create mode 100644 gitops/argocd/application.yaml create mode 100644 gitops/charts/onelab/Chart.yaml create mode 100644 gitops/charts/onelab/files/advanced.conf create mode 100644 gitops/charts/onelab/files/configurations.gotmpl create mode 100644 gitops/charts/onelab/files/enable_plugins create mode 100644 gitops/charts/onelab/files/error-404.html create mode 100644 gitops/charts/onelab/files/nginx.conf.tpl create mode 100644 gitops/charts/onelab/files/rabbit-definitions.json create mode 100644 gitops/charts/onelab/files/rabbit.conf create mode 100644 gitops/charts/onelab/templates/_helpers.tpl create mode 100644 gitops/charts/onelab/templates/configmap-rabbit.yaml create mode 100644 gitops/charts/onelab/templates/configmap-revproxy.yaml create mode 100644 gitops/charts/onelab/templates/deployment-optional-workers.yaml create mode 100644 gitops/charts/onelab/templates/deployment-redis.yaml create mode 100644 gitops/charts/onelab/templates/deployment-revproxy.yaml create mode 100644 gitops/charts/onelab/templates/secret-configurations.yaml create mode 100644 gitops/charts/onelab/templates/secret-postgres.yaml create mode 100644 gitops/charts/onelab/templates/secret-rabbit-tls.yaml create mode 100644 gitops/charts/onelab/templates/service-db.yaml create mode 100644 gitops/charts/onelab/templates/service-rabbitmq.yaml create mode 100644 gitops/charts/onelab/templates/statefulset-postgres.yaml create mode 100644 gitops/charts/onelab/templates/statefulset-rabbitmq.yaml create mode 100644 gitops/charts/onelab/templates/workloads.yaml create mode 100644 gitops/charts/onelab/values.yaml create mode 100644 gitops/values/k3s-example.yaml create mode 100644 install.sh create mode 100644 resources/restore/restore.sh create mode 100644 resources/restore/restore.yml create mode 100644 resources/scripts/centos/install_ansible.sh create mode 100644 resources/scripts/centos/install_docker.sh create mode 100644 resources/scripts/debian10/install_ansible.sh create mode 100644 resources/scripts/debian10/install_docker.sh create mode 100644 resources/scripts/debian11/install_ansible.sh create mode 100644 resources/scripts/debian11/install_docker.sh create mode 100644 resources/scripts/debian12/install_ansible.sh create mode 100644 resources/scripts/debian12/install_docker.sh create mode 100644 resources/scripts/fedora/install_ansible.sh create mode 100644 resources/scripts/fedora/install_docker.sh create mode 100644 resources/scripts/redhat7/install_ansible.sh create mode 100644 resources/scripts/redhat7/install_docker.sh create mode 100644 resources/scripts/redhat8/install_ansible.sh create mode 100644 resources/scripts/redhat8/install_docker.sh create mode 100644 resources/scripts/redhat9/install_ansible.sh create mode 100644 resources/scripts/redhat9/install_docker.sh create mode 100644 resources/scripts/ubuntu/install_ansible.sh create mode 100644 resources/scripts/ubuntu/install_docker.sh create mode 100644 resources/services/onelab.service diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1a9b223 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.env +.env.* +*.local.yaml +.DS_Store +Thumbs.db diff --git a/app/backup.sh b/app/backup.sh new file mode 100644 index 0000000..3b919ab --- /dev/null +++ b/app/backup.sh @@ -0,0 +1,3 @@ + +#!/bin/bash +ANSIBLE_LOG_PATH=./logs/ansible/$(date +%F)-ansible.log ansible-playbook ./installation/1.27.0/app/playbooks/backup.yml diff --git a/app/configurations.yml b/app/configurations.yml new file mode 100644 index 0000000..e8c7474 --- /dev/null +++ b/app/configurations.yml @@ -0,0 +1,154 @@ +--- +onelab: + domain: https://localhost + logs: + # path: "/path/to/onelab/logs" + level: info + assets: + # path: "/path/to/onelab/data" + purge: 1d + shared: + inputs: + path: "./data/shared/inputs" + archive_path: "./data/shared/archived" + security: + cors: '*' + auth: + token: + expiration: 5m + key: TokenAuthPlaceholder + password: + expiration: 90d + min_length: 8 + prevent_reuse: 5 + allow_list: [] + block_list: [] + authentifier: "email" + ratelimit: + ip: + max: 1000 + duration: 1d + auth: + max: 5 + duration: 5m + delay_after: 2 + delay_ms: 1000 + devices: + cors: '*' + monitoring: + token: TokenMonitoringPlaceholder + params: + session: + idle: 45m + remember_me: true + lab: + creation_policy: many + # compliance: + # require_electronic_signature: true + # execution_operator_restriction_policy: 'reviewed' + # execution_admin_expert_restriction_policy: 'reviewed' + # prevent_csv_import: true + # prevent_manual_metadata_edit: true + # device_restart: true + signup: false + # Google Analytics + # ga: XXXXX + # Intercom Andrew Alliance + intercom: + appid: zxvgsagz + secret: QUw2jEV8utIpe9DeYjOqBjhBY9VxjXddKUCISUNu + # Recaptcha Andrew Alliance + # recaptcha: + # client: XXXXXXXXXXXX + # secret: XXXXXXXXXXXX + #links: + # terms: https://www.andrewalliance.com/onelab-service-agreement.pdf + # privacy: https://www.andrewalliance.com/onelab-privacy-policy.pdf + # cookies: https://www.andrewalliance.com/onelab-cookie-policy.pdf + mailer: + # + # SMTP + # smtp: + # host: XXX (optional) + # port: XXX (optional) + # auth: (optional) + # user: XXX + # pass: XXX + # type: custom | login | oauth2 (optional) + # method: XXX (optional) + # secure: true | false (optional) + # + # Amazon SES + # ses: + # accessKeyId: XXXX + # secretAccessKey: XXXX + # + # MailGun + # mailgun: + # auth: + # api: XXXX + # domain: XXXX + # + # Debug + # debug: + # type: file | mail + # path: XXXX + # redirect: XXX@andrewalliance.com + # + noreply: no-reply@andrewalliance.com + queue: + scheduling: 15 + maxsize: 50 + error: + maxtries: 3 + timeout: 60 + ldap: + enabled: false + # timeout: 10 + # encryption: plain | tls | start_tls + # policy: all | changes_only + # verify_certificates: true | false + # tls: + # ca: file_path + # cert: file_path + # key: file_path + # ciphers: string + # ssl_version: string + services: + db: + # host: db + # Note: port 5432 is the standard port for postgres however a custom port can be used if 5432 is occupied already + # port: 5432 + database: postgres + username: postgres + password: DBPasswordPlaceholder + schema: onelab + # replicas: 1 + # redis: + # host: redis + # port: '6379' + # replicas: 1 + rabbit: + # url: rabbitmq + # port: 5671 + token: TokenRabbitPlaceholder + # replicas: 1 + api: + replicas: 2 + # apidevice: + # replicas: 1 + # apirabbit: + # replicas: 1 + # devices: + # replicas: 1 + # experiments: + # replicas: 1 + # images: + # replicas: 1 + # manual: + # replicas: 1 + # website: + # ssr: false + # ws: + # replicas: 1 + diff --git a/app/docker-compose.yml b/app/docker-compose.yml new file mode 100644 index 0000000..ed09b6e --- /dev/null +++ b/app/docker-compose.yml @@ -0,0 +1,319 @@ +version: "3.3" +services: + # If the database isn't accessed other than by services present in this file, remove port instruction. + # It exposes the database to the host system and if the server isn't secure, it exposes the database to attacks. + # That doesn't exclude the need to secure the servers on which the containers are run. + db: + image: hub.andrewalliance.com/releases/postgres:17.8 + volumes: + - pgdata:/var/lib/postgresql/data + environment: + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD={{ onelab.services.db.password }} + - POSTGRES_DB=postgres +{% if onelab.services.db.port is defined %} + ports: + - "{{ onelab.services.db.port }}:5432" +{% endif %} + deploy: + replicas: {{ onelab.services.db.replicas|default('1') }} + placement: + constraints: + - node.role == manager + redis: + image: hub.andrewalliance.com/releases/redis:7.4.7-alpine + deploy: + replicas: {{ onelab.services.db.redis.replicas|default('1') }} + placement: + constraints: + - node.role == manager + rabbitmq: + image: hub.andrewalliance.com/releases/rabbitmq:3.13.7 + hostname: "onelab" + volumes: + - rabbitmq_data:/var/lib/rabbitmq/mnesia + configs: + - source: enable_plugins + target: /etc/rabbitmq/enabled_plugins + - source: rabbit.conf + target: /etc/rabbitmq/rabbitmq.conf + - source: advanced.conf + target: /etc/rabbitmq/advanced.conf + - source: definitions.json + target: /opt/definitions.json + - source: rabbit.crt + target: /etc/rabbitmq/ssl/rabbit.crt + - source: rabbit.key + target: /etc/rabbitmq/ssl/rabbit.key + - source: rabbit.fullchain.pem + target: /etc/rabbitmq/ssl/rabbit.fullchain.pem + ports: + - "5671:5671" + deploy: + replicas: {{ onelab.services.rabbit.replicas|default('1') }} + placement: + constraints: + - node.role == manager + supervisor: + image: hub.andrewalliance.com/releases/onelab-supervisor-worker:1.27.0 + volumes: + - {{ onelab.logs.path|default('./logs') }}:/logs + - {{ onelab.assets.path|default('./data') }}:/data + configs: + - source: configurations.yml + target: /conf/configurations.yml + deploy: + replicas: 1 + placement: + constraints: + - node.role == {{ deploy_on|default('manager') }} + file-worker: + image: hub.andrewalliance.com/releases/onelab-file-worker:1.27.0 + volumes: + - {{ onelab.logs.path|default('./logs') }}:/logs + - {{ onelab.assets.path|default('./data') }}:/data + - {{ onelab.shared.inputs.path|default('./data/shared/inputs') }}:/shared-inputs + - {{ onelab.shared.inputs.archived_path|default('./data/shared/archived') }}:/shared-archived + configs: + - source: configurations.yml + target: /conf/configurations.yml + deploy: + replicas: 1 + placement: + constraints: + - node.role == manager + api: + image: hub.andrewalliance.com/releases/onelab-api:1.27.0 + volumes: + - {{ onelab.logs.path|default('./logs') }}:/logs + - {{ onelab.assets.path|default('./data') }}:/data + configs: + - source: configurations.yml + target: /conf/configurations.yml + deploy: + replicas: {{ onelab.services.api.replicas|default('1') }} + placement: + constraints: + - node.role == {{ deploy_on|default('manager') }} + api-device: + image: hub.andrewalliance.com/releases/onelab-api-device:1.27.0 + volumes: + - {{ onelab.logs.path|default('./logs') }}:/logs + - {{ onelab.assets.path|default('./data') }}:/data + configs: + - source: configurations.yml + target: /conf/configurations.yml + deploy: + replicas: {{ onelab.services.apidevice.replicas|default('1') }} + placement: + constraints: + - node.role == {{ deploy_on|default('manager') }} + api-rabbit: + image: hub.andrewalliance.com/releases/onelab-api-rabbit:1.27.0 + volumes: + - {{ onelab.logs.path|default('./logs') }}:/logs + - {{ onelab.assets.path|default('./data') }}:/data + configs: + - source: configurations.yml + target: /conf/configurations.yml + deploy: + replicas: {{ onelab.services.apirabbit.replicas|default('1') }} + placement: + constraints: + - node.role == {{ deploy_on|default('manager') }} + devices-worker: + image: hub.andrewalliance.com/releases/onelab-devices-worker:1.27.0 + volumes: + - {{ onelab.logs.path|default('./logs') }}:/logs + - {{ onelab.assets.path|default('./data') }}:/data + configs: + - source: configurations.yml + target: /conf/configurations.yml + deploy: + replicas: {{ onelab.services.devices.replicas|default('1') }} + placement: + constraints: + - node.role == {{ deploy_on|default('manager') }} + experiments-worker: + image: hub.andrewalliance.com/releases/onelab-experiments-worker:1.27.0 + volumes: + - {{ onelab.logs.path|default('./logs') }}:/logs + configs: + - source: configurations.yml + target: /conf/configurations.yml + deploy: + replicas: {{ onelab.services.experiments.replicas|default('1') }} + placement: + constraints: + - node.role == {{ deploy_on|default('manager') }} + images-worker: + image: hub.andrewalliance.com/releases/onelab-images-worker:1.27.0 + volumes: + - {{ onelab.logs.path|default('./logs') }}:/logs + - {{ onelab.assets.path|default('./data') }}:/data + configs: + - source: configurations.yml + target: /conf/configurations.yml + deploy: + replicas: {{ onelab.services.images.replicas|default('1') }} + placement: + constraints: + - node.role == {{ deploy_on|default('manager') }} + +{% if (onelab.ldap|default(false)) != false %} + ldap-worker: + image: hub.andrewalliance.com/releases/onelab-ldap-worker:1.27.0 + volumes: + - {{ onelab.logs.path|default('./logs') }}:/logs + configs: + - source: configurations.yml + target: /conf/configurations.yml +{% if onelab.ldap.tls.ca is defined %} + - source: ldap-ca.crt + target: /ldap/ca.crt +{% endif %} +{% if onelab.ldap.tls.key is defined %} + - source: ldap-private.key + target: /ldap/private.key +{% endif %} +{% if onelab.ldap.tls.cert is defined %} + - source: ldap-cert.crt + target: /ldap/cert.crt +{% endif %} + deploy: + replicas: {{ onelab.services.ldap.replicas|default('1') }} + placement: + constraints: + - node.role == {{ deploy_on|default('manager') }} +{% endif %} +{% if (onelab.mailer.smtp|default(false)) != false or (onelab.mailer.ses|default(false)) != false %} + mailer-worker: + image: hub.andrewalliance.com/releases/onelab-mailer-worker:1.27.0 + volumes: + - {{ onelab.logs.path|default('./logs') }}:/logs + configs: + - source: configurations.yml + target: /conf/configurations.yml + deploy: + replicas: {{ onelab.services.mailer.replicas|default('1') }} + placement: + constraints: + - node.role == {{ deploy_on|default('manager') }} +{% endif %} + manual-worker: + image: hub.andrewalliance.com/releases/onelab-manual-worker:1.27.0 + volumes: + - {{ onelab.logs.path|default('./logs') }}:/logs + configs: + - source: configurations.yml + target: /conf/configurations.yml + deploy: + replicas: {{ onelab.services.manual.replicas|default('1') }} + placement: + constraints: + - node.role == {{ deploy_on|default('manager') }} + websocket-worker: + image: hub.andrewalliance.com/releases/onelab-websocket-worker:1.27.0 + volumes: + - {{ onelab.logs.path|default('./logs') }}:/logs + configs: + - source: configurations.yml + target: /conf/configurations.yml + deploy: + replicas: {{ onelab.services.ws.replicas|default('1') }} + placement: + constraints: + - node.role == {{ deploy_on|default('manager') }} + static: + image: hub.andrewalliance.com/releases/onelab-static:1.27.0 + deploy: + replicas: 1 + placement: + constraints: + - node.role == {{ deploy_on|default('manager') }} + main: + image: hub.andrewalliance.com/releases/onelab-main:1.27.0 + deploy: + replicas: 1 + placement: + constraints: + - node.role == {{ deploy_on|default('manager') }} + designer: + image: hub.andrewalliance.com/releases/onelab-designer:1.27.0 + deploy: + replicas: 1 + placement: + constraints: + - node.role == {{ deploy_on|default('manager') }} + runner: + image: hub.andrewalliance.com/releases/onelab-runner:1.27.0 + deploy: + replicas: 1 + placement: + constraints: + - node.role == {{ deploy_on|default('manager') }} + website: + image: hub.andrewalliance.com/releases/onelab-website:1.27.0 +{% if (onelab.services.website.ssr|default(true)) != true %} + environment: + - RENDERING_MODE=no-ssr +{% endif %} + deploy: + replicas: 1 + placement: + constraints: + - node.role == {{ deploy_on|default('manager') }} + revproxy: + image: hub.andrewalliance.com/releases/nginx:1.29.5-alpine + ports: + - "8080:80" + volumes: + - {{ onelab.assets.path|default('./data') }}:/data + configs: + - source: nginx.conf + target: /etc/nginx/nginx.conf + - source: error-404.html + target: /data/error-404.html + deploy: + replicas: 1 + placement: + constraints: + - node.role == {{ deploy_on|default('manager') }} +volumes: + pgdata: + driver: local + rabbitmq_data: + driver: local +configs: + configurations.yml: + file: ./configurations.yml + nginx.conf: + file: ./nginx/onelab.conf + error-404.html: + file: ./proxy/error-404.html + rabbit.crt: + file: ./rabbit/ssl/rabbit.crt + rabbit.key: + file: ./rabbit/ssl/rabbit.key + rabbit.fullchain.pem: + file: ./rabbit/ssl/rabbit.fullchain.pem + enable_plugins: + file: ./rabbit/enable_plugins + rabbit.conf: + file: ./rabbit/rabbit.conf + advanced.conf: + file: ./rabbit/advanced.conf + definitions.json: + file: ./rabbit/definitions.json +{% if onelab.ldap.tls.ca is defined %} + ldap-ca.crt: + file: {{ onelab.ldap.tls.ca }} +{% endif %} +{% if onelab.ldap.tls.key is defined %} + ldap-private.key: + file: {{ onelab.ldap.tls.key }} +{% endif %} +{% if onelab.ldap.tls.cert is defined %} + ldap-cert.crt: + file: {{ onelab.ldap.tls.cert }} +{% endif %} diff --git a/app/nginx/onelab.conf b/app/nginx/onelab.conf new file mode 100644 index 0000000..b7c456f --- /dev/null +++ b/app/nginx/onelab.conf @@ -0,0 +1,337 @@ + +worker_processes 4; + +events { worker_connections 1024; } + +http { + include /etc/nginx/mime.types; + + client_max_body_size 2m; + + # don't send the nginx version number in error pages and Server header + server_tokens off; + + sendfile on; + + upstream website { + server website:4000; + } + + upstream main { + server main:80; + } + + upstream api { + server api:3000; + } + + upstream api-device { + server api-device:3000; + } + + upstream api-rabbit { + server api-rabbit:3000; + } + + upstream designer { + server designer:80; + } + + upstream runner { + server runner:80; + } + + upstream static { + server static:80; + } + + upstream websocket-worker { + server websocket-worker:3030; + } + + # Restore the real client IP from the upstream reverse proxy + # Trust all RFC 1918 private ranges (covers any Docker network config) + set_real_ip_from 10.0.0.0/8; + set_real_ip_from 172.16.0.0/12; + set_real_ip_from 192.168.0.0/16; + real_ip_header X-Real-IP; + + limit_req_zone $binary_remote_addr zone=auth:10m rate=1r/s; + limit_req_zone $binary_remote_addr zone=website:10m rate=5r/s; + limit_req_zone $binary_remote_addr zone=global:10m rate=10r/s; + + # redirect all http traffic to https + server { + listen 80 default_server; +{% if (onelab.services.revproxy.ipv6|default(true)) != false %} + listen [::]:80 default_server; +{% endif %} + server_name localhost; + + gzip on; + gzip_vary on; + gzip_min_length 10240; + gzip_proxied expired no-cache no-store private auth; + gzip_types text/plain text/css application/json application/x-javascript application/javascript text/xml application/xml application/xml+rss text/javascript; + gzip_disable "MSIE [1-6]\."; + + root /data/; + + location ^~ /lab/ { + rewrite ^/lab/(.*?) /app/lab/$1 last; + } + + location ^~ /assets/ { + location ~* \.(?:css|js|woff|woff2|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ { + expires 1M; + access_log off; + add_header Cache-Control "public"; + + try_files $uri $uri/ /error-404.html =404; + } + expires -1; + add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; + + try_files $uri $uri/ /error-404.html =404; + } + + location ^~ /static/ { + location ~* \.(?:css|js|woff|woff2|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ { + expires 1M; + access_log off; + add_header Cache-Control "public"; + + proxy_pass http://static; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + expires -1; + add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; + + proxy_pass http://static; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location ^~ /sitemap.xml { + limit_req zone=global burst=10 nodelay; + + proxy_pass http://api; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location ^~ /robots.txt { + limit_req zone=global burst=10 nodelay; + + proxy_pass http://api; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location ^~ /api/ { + limit_req zone=global burst=10 nodelay; + + proxy_pass http://api; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location ^~ /api/v1/auth/ { + # apply rate limiting + limit_req zone=auth burst=5 nodelay; + + proxy_pass http://api; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location ^~ /api/v1/firmwares { + limit_req zone=global burst=10 nodelay; + + client_max_body_size 600M; + + proxy_pass http://api; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location ^~ /api/v1/rmq/ { + proxy_pass http://api-rabbit; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location ^~ /device-api/ { + limit_req zone=global burst=10 nodelay; + client_max_body_size 600m; + + proxy_pass http://api-device; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location ^~ /app/designer/ { + location ~* \.(?:css|js|woff|woff2|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ { + expires 1M; + access_log off; + add_header Cache-Control "public"; + + proxy_pass http://designer; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + expires -1; + add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; + + proxy_pass http://designer; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location ^~ /app/runner/ { + location ~* \.(?:css|js|woff|woff2|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ { + expires 1M; + access_log off; + add_header Cache-Control "public"; + + proxy_pass http://runner; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + expires -1; + add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; + + proxy_pass http://runner; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location ^~ /ws/ { + proxy_pass http://websocket-worker; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + location ^~ /app/ { + location ~* \.(?:css|js|woff|woff2|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ { + expires 1M; + access_log off; + add_header Cache-Control "public"; + + proxy_pass http://main; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + expires -1; + add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; + + proxy_pass http://main; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + # Static assets for website (including /media/ subdirectory) + location ~* ^/.+\.(?:css|js|woff|woff2|ttf|eot|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ { + expires 1M; + access_log off; + add_header Cache-Control "public"; + + proxy_pass http://website; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + # Website SSR routes (rate limited) + location ~ ^/(library(/[^/]+)?|login(/(reset|change))?|signup)?$ { + limit_req zone=website burst=10 nodelay; + + expires -1; + add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; + + proxy_pass http://website; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + # Catch-all for junk requests + location / { + try_files $uri $uri/ /error-404.html =404; + } + } +} diff --git a/app/playbooks/backup.yml b/app/playbooks/backup.yml new file mode 100644 index 0000000..f14143e --- /dev/null +++ b/app/playbooks/backup.yml @@ -0,0 +1,11 @@ +--- +- name: Backing up OneLab (1.27.0) + hosts: localhost + gather_facts: yes + vars: + onelab_path: '../../../..' + + # Call from ./installation/latest/app/playbooks/backup.yml + + tasks: + - include_tasks: ./tasks/backup-task.yml diff --git a/app/playbooks/deploy.yml b/app/playbooks/deploy.yml new file mode 100644 index 0000000..75f0131 --- /dev/null +++ b/app/playbooks/deploy.yml @@ -0,0 +1,18 @@ +--- +- name: Deploying OneLab (1.27.0) + hosts: localhost + vars: + onelab_path: '../../../../../onelab' + + # Call from ./installation/latest/app/playbooks/deploy.yml + + tasks: + # Load configuration + - include_vars: + file: "{{ onelab_path }}/configurations.yml" + + # Start proxy + - include_tasks: ./tasks/start-proxy.yml + + # Start OneLab + - include_tasks: ./tasks/start-onelab.yml diff --git a/app/playbooks/install-swarm.yml b/app/playbooks/install-swarm.yml new file mode 100644 index 0000000..bcab1ad --- /dev/null +++ b/app/playbooks/install-swarm.yml @@ -0,0 +1,25 @@ +--- +- name: Generate the command to join a node as swarm worker + hosts: localhost + gather_facts: yes + + tasks: + - name: Determine Docker swarm status + shell: > + docker info | egrep 'Swarm: ' + register: swarm_status + become: true + + - name: Prepare the command to join a node as worker in this swarm + command: docker swarm join-token worker -q + become: true + when: "' Swarm: active' in swarm_status.stdout_lines" + register: worker_join_token + + - set_fact: + run_on_worker: "sudo docker swarm join --token {{ worker_join_token.stdout }} {{ ansible_eth0.ipv4.address }}:2377" + + - debug: var=run_on_worker + + + diff --git a/app/playbooks/install.yml b/app/playbooks/install.yml new file mode 100644 index 0000000..bc1d5f6 --- /dev/null +++ b/app/playbooks/install.yml @@ -0,0 +1,13 @@ +--- +- name: Installing OneLab (1.27.0) + hosts: localhost + gather_facts: yes + vars: + main_path: '../../..' + onelab_path: '../../../onelab' + installer_path: '../../../onelab-enterprise-installer-1.27.0' + + # Call from ./onelab-enterprise-installer-1.27.0/app/playbooks/install.yml + + tasks: + - include_tasks: ./tasks/install-task.yml diff --git a/app/playbooks/reconfigure.yml b/app/playbooks/reconfigure.yml new file mode 100644 index 0000000..38cc208 --- /dev/null +++ b/app/playbooks/reconfigure.yml @@ -0,0 +1,15 @@ +--- +- name: Reconfiguring OneLab (1.27.0) + hosts: localhost + vars: + main_path: '../../../../..' + onelab_path: '../../../../../onelab' + + # Call from ./installation/latest/app/playbooks/reconfigure.yml + + tasks: + # Load configuration + - include_vars: + file: "{{ onelab_path }}/configurations.yml" + + - include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/reconfigure-task.yml" diff --git a/app/playbooks/stop.yml b/app/playbooks/stop.yml new file mode 100644 index 0000000..ac16367 --- /dev/null +++ b/app/playbooks/stop.yml @@ -0,0 +1,12 @@ +--- +- name: Stopping OneLab (1.27.0) + hosts: localhost + vars: + onelab_path: '../../../../../onelab' + + # Call from ./installation/latest/app/playbooks/stop.yml + + tasks: + - include_tasks: ./tasks/stop-onelab.yml + + - include_tasks: ./tasks/stop-proxy.yml diff --git a/app/playbooks/tasks/backup-task.yml b/app/playbooks/tasks/backup-task.yml new file mode 100644 index 0000000..2208fec --- /dev/null +++ b/app/playbooks/tasks/backup-task.yml @@ -0,0 +1,37 @@ +--- +- name: Checking if OneLab is running + shell: docker stack ls | grep 'onelab' | grep -v 'onelab_proxy' | wc -l + become: true + register: backup_stack_count + +# Start DB if OneLab is not running +- include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/start-db-only.yml" + when: backup_stack_count.stdout == "0" + +- name: Creating backup directory + file: + path: "{{ onelab_path }}/backups/{{ ansible_date_time.iso8601_basic_short }}_1.27.0" + state: directory + register: backup_path + +- name: Backing up files + shell: + tar --exclude='temp' -czvf {{ backup_path.path }}/onelab.tar.gz -C {{ onelab_path }} ./data ./installation ./nginx ./proxy ./rabbit ./ssl ./backup.sh ./configurations.yml ./docker-compose.yml ./reconfigure.sh ./start.sh ./stop.sh ./update.sh + become: yes + +- name: Backing up database + shell: + docker exec -i $(docker ps --filter "name=onelab_db|onelab-db" -q) pg_dump -Upostgres -Fc > {{ backup_path.path }}/db.tar.gz + become: yes + +- name: Creating restore script files + copy: + src: "{{ onelab_path }}/installation/latest/resources/restore/{{ item }}" + dest: "{{ backup_path.path }}" + with_items: + - 'restore.yml' + - 'restore.sh' + +# Stop DB if OneLab was not running +- include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/stop-db-only.yml" + when: backup_stack_count.stdout == "0" diff --git a/app/playbooks/tasks/check-offline-resources.yml b/app/playbooks/tasks/check-offline-resources.yml new file mode 100644 index 0000000..4e84578 --- /dev/null +++ b/app/playbooks/tasks/check-offline-resources.yml @@ -0,0 +1,7 @@ +--- +# Check if Docker images are existing locally +- name: Checking local Docker images bundle + stat: + path: "{{ main_path }}/onelab-images-1.27.0.tar.gz" + get_checksum: no + register: docker_images_tar diff --git a/app/playbooks/tasks/check-requirements.yml b/app/playbooks/tasks/check-requirements.yml new file mode 100644 index 0000000..83648ca --- /dev/null +++ b/app/playbooks/tasks/check-requirements.yml @@ -0,0 +1,71 @@ +--- +- name: Checking internet connection + uri: + url: "https://hub.andrewalliance.com" + register: pingresult + until: + - pingresult.status == 200 + retries: 3 # 3 * 5 seconds = 15s + delay: 5 # Every 5 seconds + when: (docker_images_tar is undefined) or (not docker_images_tar.stat.exists) + +- name: Getting current logged-in user + shell: whoami + register: login_user +- debug: var=login_user.stdout + +# User running the process may have to enter their sudo password at this step + +- name: Checking if the logged-in user has sudo privileges + shell: sudo whoami + register: sudo_check + failed_when: sudo_check.stdout != "root" + +- name: Checking Ansible + debug: + msg: "{{ item }}" + with_items: + - "{{ ansible_distribution }}" + - "{{ ansible_distribution_version }}" + - "{{ ansible_distribution_major_version }}" + +- name: Checking Docker version + shell: docker -v | cut -d ' ' -f 3 | cut -d ',' -f 1 + become: true + register: docker_version +- debug: var=docker_version.stdout + +# workaround: add a dumb failed condition to avoid any failure here as the service is maybe not installed/existing +- name: Check if "docker compose" is available + shell: docker compose version + become: true + register: docker_compose_check + failed_when: "'FAILED' in docker_compose_check.stderr" + +- name: Set the compose command to "docker compose" + set_fact: + compose_command: "docker compose" + when: docker_compose_check.rc == 0 + +- name: Fallback to "docker-compose" if "docker compose" is not available + set_fact: + compose_command: "docker-compose" + when: docker_compose_check.rc != 0 + +- name: Checking Docker Compose version + shell: "{{ compose_command }} version" + become: true + register: compose_version_output +- debug: var=compose_version_output.stdout + +- name: Checking tar pacakge + shell: tar --usage + become: true + +- name: Checking unzip pacakge + shell: unzip + become: true + +- name: Checking openssl package + shell: openssl + become: true diff --git a/app/playbooks/tasks/check-ssl-files.yml b/app/playbooks/tasks/check-ssl-files.yml new file mode 100644 index 0000000..ff9d5f9 --- /dev/null +++ b/app/playbooks/tasks/check-ssl-files.yml @@ -0,0 +1,49 @@ +--- +# Check SSL files +- name: Getting the list of Docker secrets + shell: docker secret ls | tail -n +2 | awk '{print $2}' + register: docker_secrets + become: true + +- name: Checking if dhparam.pem file exists in /onelab/ssl + stat: + path: "{{ onelab_path }}/ssl/dhparam.pem" + register: dhparam + failed_when: not dhparam.stat.exists + +- name: Checking if server.pem file exists in /onelab/ssl + stat: + path: "{{ onelab_path }}/ssl/server.pem" + register: serverpem + failed_when: not serverpem.stat.exists + +- name: Checking if server.key file exists in /onelab/ssl + stat: + path: "{{ onelab_path }}/ssl/server.key" + register: serverkey + failed_when: not serverkey.stat.exists + +- name: Checking if chain.pem file exists in /onelab/ssl + stat: + path: "{{ onelab_path }}/ssl/chain.pem" + register: chain + failed_when: not chain.stat.exists + +- name: Checking if certificate and key are matching (part 1/2) + shell: > + openssl rsa -noout -modulus -in {{ onelab_path }}/ssl/server.key | openssl md5 + register: serverkey_modulus + become: true + when: + - "'ssl_passphrase' not in docker_secrets.stdout_lines" + - (onelab.deployment.skip_ssl is undefined) or (onelab.deployment.skip_ssl == false) + +- name: Checking if certificate and key are matching (part 2/2) + shell: > + openssl x509 -noout -modulus -in {{ onelab_path }}/ssl/server.pem | openssl md5 + register: serverpem_modulus + failed_when: serverpem_modulus.stdout != serverkey_modulus.stdout + become: true + when: + - "'ssl_passphrase' not in docker_secrets.stdout_lines" + - (onelab.deployment.skip_ssl is undefined) or (onelab.deployment.skip_ssl == false) diff --git a/app/playbooks/tasks/init-docker.yml b/app/playbooks/tasks/init-docker.yml new file mode 100644 index 0000000..0c093ba --- /dev/null +++ b/app/playbooks/tasks/init-docker.yml @@ -0,0 +1,21 @@ +--- +# +# Configure Docker +# + +- name: Ensuring Docker status + service: + name: "docker" + state: started + +- name: Determining Docker swarm status + shell: > + docker info | egrep 'Swarm: ' + register: swarm_status + become: true + +- name: Configuring Docker Swarm + shell: > + docker swarm init --advertise-addr "{{ ansible_default_ipv4.address }}" + when: "' Swarm: inactive' in swarm_status.stdout_lines" + become: true diff --git a/app/playbooks/tasks/init-onelab.yml b/app/playbooks/tasks/init-onelab.yml new file mode 100644 index 0000000..6b8314a --- /dev/null +++ b/app/playbooks/tasks/init-onelab.yml @@ -0,0 +1,119 @@ +--- +# +# Start Installation +# + +# Folders creation +- name: Creating OneLab directory + file: + path: "{{ onelab_path }}" + state: directory + +- name: Creating OneLab directory structure + file: + path: "{{ onelab_path }}/{{ item }}" + state: directory + with_items: + - 'backups' + - 'data' + - 'data/shared' + - 'data/shared/inputs' + - 'data/shared/archived' + - 'logs' + - 'installation' + - 'ssl' + - 'rabbit' + +- name: Creating directory for rabbit SSL certificates + file: + path: "{{ onelab_path }}/rabbit/ssl" + state: directory + +- name: Creating directory for Ansible logs + file: + path: "{{ onelab_path }}/logs/ansible" + state: directory + +# Copy version +- name: Cleaning latest installation + ansible.builtin.file: + path: "{{ onelab_path }}/installation/latest" + state: absent + +- name: Copying current installation bundle + ansible.builtin.copy: + src: "{{ installer_path }}/" + dest: "{{ onelab_path }}/installation/{{ item }}" + directory_mode: no + remote_src: yes + with_items: + - 'latest' + - '1.27.0' + +# Copy files +- name: Copying applicative files + copy: + src: "{{ onelab_path }}/installation/latest/app/{{ item }}" + dest: "{{ onelab_path }}/" + directory_mode: yes + remote_src: yes + with_items: + - 'nginx' + - 'proxy' + - 'rabbit' + +- name: Copying routine scripts + copy: + src: "{{ onelab_path }}/installation/latest/app/{{ item }}.sh" + dest: "{{ onelab_path }}/" + directory_mode: yes + remote_src: yes + with_items: + - 'start' + - 'stop' + - 'reconfigure' + - 'backup' + - 'update' + + +- name: Initializing custom config proxy files + file: + path: "{{ onelab_path }}/proxy/{{ item }}" + state: touch + with_items: + - 'custom-http.conf' + - 'custom-server.conf' + when: not is_update + +# Initialize configurations.yml file +- name: Initializing configurations.yml file + copy: + src: "{{ onelab_path }}/installation/latest/app/configurations.yml" + dest: "{{ onelab_path }}/configurations.yml" + remote_src: yes + when: not is_update + +- name: Initializing unique Password and Tokens + ansible.builtin.replace: + path: "{{ onelab_path }}/configurations.yml" + regexp: '{{ item.placeholder }}' + replace: '{{ item.value }}' + with_items: + - { placeholder: DBPasswordPlaceholder, value: '{{ lookup("password", "/dev/null length=16 chars=ascii_letters,digits") }}' } + - { placeholder: TokenAuthPlaceholder, value: '{{ lookup("password", "/dev/null length=32 chars=ascii_letters,digits") }}' } + - { placeholder: TokenRabbitPlaceholder, value: '{{ lookup("password", "/dev/null length=32 chars=ascii_letters,digits") }}' } + - { placeholder: TokenMonitoringPlaceholder, value: '{{ lookup("password", "/dev/null length=32 chars=ascii_letters,digits") }}' } + when: not is_update + +- name: Initializing Rabbit SSL certificate + shell: | + openssl req -x509 -nodes -days 7300 -newkey rsa:2048 -keyout {{ onelab_path }}/rabbit/ssl/rabbit.key -out {{ onelab_path }}/rabbit/ssl/rabbit.crt -subj "/C=CH/ST=Geneva/L=Geneva/O=Andrew Alliance/OU=OneLab/CN=andrewalliance.com" + cat {{ onelab_path }}/rabbit/ssl/rabbit.crt > {{ onelab_path }}/rabbit/ssl/rabbit.fullchain.pem + become: true + when: not is_update + +- name: Initializing DH Param + shell: | + openssl dhparam -out {{ onelab_path }}/ssl/dhparam.pem 2048 + become: true + when: not is_update diff --git a/app/playbooks/tasks/init-service.yml b/app/playbooks/tasks/init-service.yml new file mode 100644 index 0000000..e9e5975 --- /dev/null +++ b/app/playbooks/tasks/init-service.yml @@ -0,0 +1,33 @@ +--- +# +# Create onelab.service +# +- name: Check ansible-playbook bin + shell: whereis ansible-playbook | cut -d ' ' -f 2 + register: ansiblebin_result + become: true + +# workaround: add a dumb failed condition to avoid any failure here as the service is maybe not installed/existing +- name: Check onelab.service + shell: | + systemctl disable onelab.service + rm /etc/systemd/system/onelab.service + register: command_result + failed_when: "'FAILED' in command_result.stderr" + become: true + +# playbook_dir will be /home/ubuntu/onelab/test/onelab-enterprise-installer-release-1.13.0/app/playbooks for install.sh +# but will be /home/ubuntu/onelab/test/onelab/installation/latest/app/playbooks/ for update.sh +- debug: var=playbook_dir + +- name: Creating onelab.service + ansible.builtin.template: + src: "{{ installer_path }}/resources/services/onelab.service" + dest: "/etc/systemd/system/onelab.service" + become: true + +- name: Configuring onelab.service + shell: | + systemctl daemon-reload + systemctl enable onelab.service + become: true diff --git a/app/playbooks/tasks/install-task.yml b/app/playbooks/tasks/install-task.yml new file mode 100644 index 0000000..690bc73 --- /dev/null +++ b/app/playbooks/tasks/install-task.yml @@ -0,0 +1,81 @@ +--- +# +# Offline-resources-check +# +- include_tasks: "{{ installer_path }}/app/playbooks/tasks/check-offline-resources.yml" +# +# Pre-check +# +- include_tasks: "{{ installer_path }}/app/playbooks/tasks/check-requirements.yml" +# +# Configure Docker +# +- include_tasks: "{{ installer_path }}/app/playbooks/tasks/init-docker.yml" + + +- name: Loading Docker images + debug: + msg: + - "Loading images could take few minutes, please do not interrupt" + when: (docker_images_tar is defined) and (docker_images_tar.stat.exists) + +- name: Loading OneLab images from the tar file + shell: + docker load --input {{ main_path }}/onelab-images-1.27.0.tar.gz + become: true + when: (docker_images_tar is defined) and (docker_images_tar.stat.exists) + + +# +# Check if we are running a fresh installation or an update on an existing installation +# +- name: Checking if OneLab is already installed + stat: + path: "{{ onelab_path }}/docker-compose.yml" + register: onelab_result + +# +# Pre-migration task +# +- include_tasks: "{{ installer_path }}/app/playbooks/tasks/pre-update.yml" + when: onelab_result.stat.exists + +# +# Install OneLab +# +- include_tasks: "{{ installer_path }}/app/playbooks/tasks/init-onelab.yml" + vars: + is_update: "{{ onelab_result.stat.exists|bool }}" + +# +# Init Service +# +- include_tasks: "{{ installer_path }}/app/playbooks/tasks/init-service.yml" + +# Checking Docker volumes + +- name: Checking if RabbitMQ exists + shell: docker volume ls + become: true + register: list_volumes + + +# Cleaning cache +- name: Cleaning RabbitMQ cache + shell: docker volume rm onelab_rabbitmq_data + become: true + when: "'onelab_rabbitmq_data' in list_volumes.stdout" + +# +# Reconfigure +# +- include_vars: + file: "{{ onelab_path }}/configurations.yml" + +- include_tasks: "{{ installer_path }}/app/playbooks/tasks/update-conf-files.yml" + +# +# Post-migration task +# +- include_tasks: "{{ installer_path }}/app/playbooks/tasks/post-update.yml" + when: onelab_result.stat.exists \ No newline at end of file diff --git a/app/playbooks/tasks/manage-images.yml b/app/playbooks/tasks/manage-images.yml new file mode 100644 index 0000000..0848e25 --- /dev/null +++ b/app/playbooks/tasks/manage-images.yml @@ -0,0 +1,40 @@ +- name: Connecting to Docker Hub + shell: > + docker login -u {{ onelab.docker.login.user|default('public') }} -p {{ onelab.docker.login.password|default('Andrew01..Release') }} hub.andrewalliance.com + become: true + when: (docker_images_tar is undefined) or (not docker_images_tar.stat.exists) + +- name: Checking current OneLab images + shell: > + {% raw %} + docker image ls --format '{{.ID}} {{.Repository}}:{{.Tag}}' | + egrep 'onelab-' | + egrep -v '1.27.0' | + awk '{ print $1 }' + {% endraw %} + register: onelab_old_images + become: true + +- name: Pruning OneLab old images + shell: + docker rmi {{ item }} + become: true + loop: "{{ onelab_old_images.stdout_lines }}" + +- name: Gathering OneLab images + shell: > + cat {{ onelab_path }}/docker-compose.yml | egrep 'image: ' | awk '{ print $2 }' + register: onelab_images + become: true + when: (docker_images_tar is undefined) or (not docker_images_tar.stat.exists) + +- name: Pulling OneLab images + shell: > + docker image pull {{ item }} + become: true + register: pullimages + loop: "{{ onelab_images.stdout_lines }}" + until: pullimages is not failed + retries: 10 + delay: 5 + when: (docker_images_tar is undefined) or (not docker_images_tar.stat.exists) diff --git a/app/playbooks/tasks/post-db-migration.yml b/app/playbooks/tasks/post-db-migration.yml new file mode 100644 index 0000000..d92983c --- /dev/null +++ b/app/playbooks/tasks/post-db-migration.yml @@ -0,0 +1,28 @@ +--- + +- name: Pull new postgres image + shell: | + docker image pull hub.andrewalliance.com/releases/postgres:17.8 + become: true + when: (docker_images_tar is undefined) or (not docker_images_tar.stat.exists) + +- name: Cleaning database + shell: > + docker volume rm onelab_pgdata + become: true + +- include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/start-db-only.yml" + +- name: Restoring & Migrating database + shell: | + docker exec -i $(docker ps --filter "name=onelab_db|onelab-db" -q) pg_restore -Upostgres -dpostgres -v -Fc < {{ onelab_path }}/db-migration-postgres.tar.gz + rm {{ onelab_path }}/db-migration-postgres.tar.gz + become: true + +- include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/stop-db-only.yml" + +- name: Removing lock file + shell: > + rm {{ onelab_path }}/.lock_db_migration + become: true + diff --git a/app/playbooks/tasks/post-update.yml b/app/playbooks/tasks/post-update.yml new file mode 100644 index 0000000..a5ce8f9 --- /dev/null +++ b/app/playbooks/tasks/post-update.yml @@ -0,0 +1,21 @@ +--- + # + # Specfic update tasks for version + # + + + # + # Migrate SSL structure (1.12.0 -> 1.13.0) + # + - include_tasks: "{{ installer_path }}/app/playbooks/tasks/ssl-cert-migration.yml" + + # + # Postgres13 post-migration + # + - name: Checking if database has to be migrated + stat: + path: "{{ onelab_path }}/.lock_db_migration" + register: onelab_lock_db_result + + - include_tasks: "{{ installer_path }}/app/playbooks/tasks/post-db-migration.yml" + when: onelab_lock_db_result.stat.exists \ No newline at end of file diff --git a/app/playbooks/tasks/pre-db-migration.yml b/app/playbooks/tasks/pre-db-migration.yml new file mode 100644 index 0000000..a029bba --- /dev/null +++ b/app/playbooks/tasks/pre-db-migration.yml @@ -0,0 +1,14 @@ +--- +- include_tasks: "{{ installer_path }}/app/playbooks/tasks/start-db-only.yml" + +- name: Backing up database + shell: + docker exec -i $(docker ps --filter "name=onelab_db|onelab-db" -q) pg_dump -Upostgres -Fc > {{ onelab_path }}/db-migration-postgres.tar.gz + become: yes + +- include_tasks: "{{ installer_path }}/app/playbooks/tasks/stop-db-only.yml" + +- name: Creating lock file + shell: > + touch {{ onelab_path }}/.lock_db_migration + become: true diff --git a/app/playbooks/tasks/pre-update.yml b/app/playbooks/tasks/pre-update.yml new file mode 100644 index 0000000..cc2ea3e --- /dev/null +++ b/app/playbooks/tasks/pre-update.yml @@ -0,0 +1,12 @@ +--- + - name: Checking current database + shell: > + cat {{ onelab_path }}/docker-compose.yml | egrep 'image: postgres' | awk '{ print $2 }' + register: onelab_db + become: true + + # + # Postgres pre-migration + # + - include_tasks: "{{ installer_path }}/app/playbooks/tasks/pre-db-migration.yml" + when: onelab_db.stdout != 'hub.andrewalliance.com/releases/postgres:17.8' diff --git a/app/playbooks/tasks/reconfigure-task.yml b/app/playbooks/tasks/reconfigure-task.yml new file mode 100644 index 0000000..cb5ae9e --- /dev/null +++ b/app/playbooks/tasks/reconfigure-task.yml @@ -0,0 +1,17 @@ +--- +# Evaluate Docker images bundle +- include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/check-offline-resources.yml" + when: (policy | default('')) != "no-pull" and (onelab.docker.policy | default('')) != "no-pull" + +# Update conf files +- include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/update-conf-files.yml" + +# Check SSL files +- include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/check-ssl-files.yml" + +# Rotate Rabbit SSL files +- include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/rotate-ssl-files-for-rabbit.yml" + +# Connect to Docker and pull images, and clean up old images +- include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/manage-images.yml" + when: (policy | default('')) != "no-pull" and (onelab.docker.policy | default('')) != "no-pull" diff --git a/app/playbooks/tasks/rotate-ssl-files-for-rabbit.yml b/app/playbooks/tasks/rotate-ssl-files-for-rabbit.yml new file mode 100644 index 0000000..8ad43c3 --- /dev/null +++ b/app/playbooks/tasks/rotate-ssl-files-for-rabbit.yml @@ -0,0 +1,8 @@ +--- +# Check SSL files +- name: Rotate Rabbit SSL certificate + shell: | + openssl req -x509 -nodes -days 7300 -newkey rsa:2048 -keyout {{ onelab_path }}/rabbit/ssl/rabbit.key -out {{ onelab_path }}/rabbit/ssl/rabbit.crt -subj "/C=CH/ST=Geneva/L=Geneva/O=Andrew Alliance/OU=OneLab/CN=andrewalliance.com" + cat {{ onelab_path }}/rabbit/ssl/rabbit.crt > {{ onelab_path }}/rabbit/ssl/rabbit.fullchain.pem + become: true + \ No newline at end of file diff --git a/app/playbooks/tasks/ssl-cert-migration.yml b/app/playbooks/tasks/ssl-cert-migration.yml new file mode 100644 index 0000000..14c4ed2 --- /dev/null +++ b/app/playbooks/tasks/ssl-cert-migration.yml @@ -0,0 +1,18 @@ +--- + +- name: Checking if fullchain.pem file exists in /onelab/ssl + stat: + path: "{{ onelab_path }}/ssl/fullchain.pem" + register: fullchain + +- name: Migrating legacy SSL structure to the new one + shell: | + cp {{ onelab_path }}/ssl/fullchain.pem {{ onelab_path }}/ssl/server.pem + cp {{ onelab_path }}/ssl/fullchain.pem {{ onelab_path }}/ssl/chain.pem + rm {{ onelab_path }}/ssl/server.crt + rm {{ onelab_path }}/ssl/fullchain.pem + when: fullchain.stat.exists + register: command_result + failed_when: "'FAILED' in command_result.stderr" + become: true + diff --git a/app/playbooks/tasks/start-db-only.yml b/app/playbooks/tasks/start-db-only.yml new file mode 100644 index 0000000..4caaf2e --- /dev/null +++ b/app/playbooks/tasks/start-db-only.yml @@ -0,0 +1,23 @@ +--- +# workaround: add a dumb failed condition to avoid any failure here as the service is maybe not installed/existing +- name: Check if "docker compose" is available + shell: docker compose version + become: true + register: docker_compose_check + failed_when: "'FAILED' in docker_compose_check.stderr" + +- name: Set the compose command to "docker compose" + set_fact: + compose_command: "docker compose" + when: docker_compose_check.rc == 0 + +- name: Fallback to "docker-compose" if "docker compose" is not available + set_fact: + compose_command: "docker-compose" + when: docker_compose_check.rc != 0 + +- name: Starting database service + shell: | + {{ compose_command }} -f {{ onelab_path }}/docker-compose.yml up -d db + sleep 30 + become: true diff --git a/app/playbooks/tasks/start-onelab.yml b/app/playbooks/tasks/start-onelab.yml new file mode 100644 index 0000000..f84bf7c --- /dev/null +++ b/app/playbooks/tasks/start-onelab.yml @@ -0,0 +1,19 @@ +--- +- name: Starting OneLab + shell: > + docker stack deploy onelab --compose-file {{ onelab_path }}/docker-compose.yml --with-registry-auth --prune + become: true + +- name: Checking OneLab health status (could take up to 5min to be alive) + uri: + url: "{{ onelab.domain }}/api/status?token={{ onelab.security.monitoring.token }}" + return_content: yes + validate_certs: no + register: json_response + until: + - json_response.status == 200 + - (json_response is defined) and (json_response.content|length > 0) + - json_response.content is search('status') + - "(json_response.content | from_json).status == 'running'" + retries: 60 # 60 * 10 seconds = 10m + delay: 10 # Every 10 seconds \ No newline at end of file diff --git a/app/playbooks/tasks/start-proxy.yml b/app/playbooks/tasks/start-proxy.yml new file mode 100644 index 0000000..ab013a3 --- /dev/null +++ b/app/playbooks/tasks/start-proxy.yml @@ -0,0 +1,6 @@ +--- +- name: Starting Proxy + shell: | + docker stack deploy onelab_proxy --compose-file {{ onelab_path }}/proxy/docker-compose.yml + sleep 10 + become: true \ No newline at end of file diff --git a/app/playbooks/tasks/stop-db-only.yml b/app/playbooks/tasks/stop-db-only.yml new file mode 100644 index 0000000..1048438 --- /dev/null +++ b/app/playbooks/tasks/stop-db-only.yml @@ -0,0 +1,6 @@ +--- +- name: Stopping database service + shell: | + {{ compose_command }} -f {{ onelab_path }}/docker-compose.yml down + sleep 30 + become: true diff --git a/app/playbooks/tasks/stop-onelab.yml b/app/playbooks/tasks/stop-onelab.yml new file mode 100644 index 0000000..42abf45 --- /dev/null +++ b/app/playbooks/tasks/stop-onelab.yml @@ -0,0 +1,6 @@ +--- +- name: Stopping OneLab + shell: | + docker stack rm onelab + sleep 30 + become: true diff --git a/app/playbooks/tasks/stop-proxy.yml b/app/playbooks/tasks/stop-proxy.yml new file mode 100644 index 0000000..1632bea --- /dev/null +++ b/app/playbooks/tasks/stop-proxy.yml @@ -0,0 +1,6 @@ +--- +- name: Stopping Proxy + shell: | + docker stack rm onelab_proxy + sleep 20 + become: true \ No newline at end of file diff --git a/app/playbooks/tasks/update-conf-files.yml b/app/playbooks/tasks/update-conf-files.yml new file mode 100644 index 0000000..7d73344 --- /dev/null +++ b/app/playbooks/tasks/update-conf-files.yml @@ -0,0 +1,24 @@ +--- +- name: Loading Docker Swarm configuration + shell: > + docker node ls | grep "Ready" | wc -l + register: nodecount + become: true + +- set_fact: + deploy_on: "{{ 'manager' if (nodecount.stdout == '1') else 'worker' }}" + +- name: Getting the list of Docker secrets + shell: docker secret ls | tail -n +2 | awk '{print $2}' + register: docker_secrets + become: true + +- name: Reconfiguring files + ansible.builtin.template: + src: "{{ onelab_path }}/installation/latest/app/{{ item }}" + dest: "{{ onelab_path }}/{{ item }}" + with_items: + - 'docker-compose.yml' + - 'nginx/onelab.conf' + - 'proxy/proxy.conf' + - 'proxy/docker-compose.yml' diff --git a/app/playbooks/uninstall.yml b/app/playbooks/uninstall.yml new file mode 100644 index 0000000..a6aac1d --- /dev/null +++ b/app/playbooks/uninstall.yml @@ -0,0 +1,53 @@ +--- +- name: Uninstalling OneLab (1.27.0) + hosts: localhost + vars_prompt: + - name: "userchoice" + prompt: "Do you want to uninstall OneLab and remove all the data? Type: yes/no" + private: no + + # Call from ./installation/latest/app/playbooks/uninstall.yml + + tasks: + + - name: Aborting the uninstall process + fail: + msg: Aborting the uninstall process + when: userchoice != "yes" + + # Stop OneLab if OneLab is running + - name: Checking if OneLab is running + shell: docker stack ls | grep 'onelab' | wc -l + become: true + register: stack_count + + - include_tasks: ./tasks/stop-onelab.yml + when: stack_count.stdout != "0" + - include_tasks: ./tasks/stop-proxy.yml + when: stack_count.stdout != "0" + + # Remove Docker volumes (onelab_pgdata, onelab_rabbitmq_data) + - name: Removing Docker data + shell: | + docker volume rm onelab_pgdata -f + docker volume rm onelab_rabbitmq_data -f + become: true + + # Remove Docker images + - name: Checking current OneLab images + shell: > + docker image ls | egrep 'onelab-' | egrep -v '1.27.0' | awk '{ print $3}' + register: onelab_old_images + become: true + + - name: Pruning OneLab old images + shell: + docker rmi {{ item }} + become: true + loop: "{{ onelab_old_images.stdout_lines }}" + + # Remove Docker login credentials + - name: Removing Docker login credentials + shell: | + docker logout hub.andrewalliance.com + become: true diff --git a/app/playbooks/update.yml b/app/playbooks/update.yml new file mode 100644 index 0000000..a0e22d7 --- /dev/null +++ b/app/playbooks/update.yml @@ -0,0 +1,115 @@ +# This playbook updates the OneLab Enterprise to latest version or a chosen version in one step +--- +- name: Updating OneLab + hosts: localhost + gather_facts: yes + vars: + main_path: '../../../../..' + onelab_path: '../../../../../onelab' + + vars_prompt: + - name: "userchoice" + prompt: "Confirm OneLab update: yes/no" + private: no + + tasks: + + # Choice to proceed or stop the update process + - name: Aborting the update process + fail: + msg: Aborting the update process + when: userchoice != "yes" + + # Gather latest version information + - name: Fetching OneLab latest version details from the repository + uri: + url: "https://hub.andrewalliance.com/enterprise/version.json" + return_content: yes + validate_certs: no + register: latest_version + become: true + when: version is undefined + - set_fact: + latest: "{{ (latest_version.content | from_json).version }}" + when: version is undefined + + - name: Checking OneLab current version + fail: + msg: OneLab is already up-to-date (1.27.0) + when: (version is undefined) and (latest == "1.27.0") + + - name: Starting OneLab update to {{ version | default(latest) }} + debug: + msg: Version {{ version | default(latest) }} + + # Remove installer bundles if it exists already, then download & unzip + - name: Removing the bundle if it already exists + file: + state: absent + path: "{{ item }}" + with_items: + - "{{ main_path }}/onelab-enterprise-installer-{{ version | default(latest)}}" + - "{{ main_path }}/onelab-enterprise-installer-{{ version | default(latest)}}.zip" + become: true + + - name: Downloading the installer bundle + shell: wget --no-check-certificate https://hub.andrewalliance.com/enterprise/onelab-enterprise-installer-{{ version | default(latest)}}.zip -P {{ main_path }} + become: true + + - name: Unzip the installer bundle + shell: unzip {{ main_path }}/onelab-enterprise-installer-{{ version | default(latest)}}.zip -d {{ main_path }}/onelab-enterprise-installer-{{ version | default(latest)}} + become: true + + - name: Checking if OneLab is running + shell: docker stack ls | grep 'onelab' | wc -l + become: true + register: stack_count + + # Stop OneLab (unkown version) if OneLab is running + - include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/stop-onelab.yml" + when: stack_count.stdout != "0" + - include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/stop-proxy.yml" + when: stack_count.stdout != "0" + + # Start Proxy (maintenance page) if OneLab was running + - include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/start-proxy.yml" + when: stack_count.stdout != "0" + + # Perform OneLab backup + - include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/backup-task.yml" + + # Perform OneLab update + - include_tasks: "{{ main_path }}/onelab-enterprise-installer-{{ version | default(latest)}}/app/playbooks/tasks/install-task.yml" + vars: + main_path: '../../../../..' + onelab_path: '../../../../../onelab' + installer_path: '../../../../../onelab-enterprise-installer-{{ version | default(latest)}}' + + # Load configuration + - include_vars: + file: "{{ onelab_path }}/configurations.yml" + + # Perform reconfiguration + # Use dynamic path (variable) to force dynamic loading of this new task + - include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/reconfigure-task.yml" + + # Remove installer bundles + - name: Removing the installer + file: + state: absent + path: "{{ item }}" + with_items: + - "{{ main_path }}/onelab-enterprise-installer-{{ version | default(latest)}}" + - "{{ main_path }}/onelab-enterprise-installer-{{ version | default(latest)}}.zip" + become: true + + # Stop Proxy (maintenance page) if OneLab was running + # Use dynamic path (variable) to force dynamic loading of this new task + - include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/stop-proxy.yml" + when: stack_count.stdout != "0" + + # Start OneLab if OneLab was running + - include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/start-proxy.yml" + when: stack_count.stdout != "0" + - include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/start-onelab.yml" + when: stack_count.stdout != "0" \ No newline at end of file diff --git a/app/proxy/docker-compose.yml b/app/proxy/docker-compose.yml new file mode 100644 index 0000000..41681e5 --- /dev/null +++ b/app/proxy/docker-compose.yml @@ -0,0 +1,47 @@ +version: "3.2" +services: + proxy: + image: hub.andrewalliance.com/releases/nginx:1.29.5-alpine + ports: + - target: 80 + published: 80 + protocol: tcp + mode: host + - target: 443 + published: 443 + protocol: tcp + mode: host + volumes: + - ./../ssl:/etc/nginx/ssl + - ./not-supported.html:/usr/onelab/not-supported.html + - ./error-404.html:/usr/onelab/error-404.html + - ./proxy.conf:/etc/nginx/nginx.conf + - ./custom-http.conf:/etc/nginx/custom-http.conf + - ./custom-server.conf:/etc/nginx/custom-server.conf + extra_hosts: + - "host.docker.internal:host-gateway" + deploy: + replicas: 1 + placement: + constraints: + - node.role == manager +{% if docker_secrets is defined and "ssl_passphrase" in docker_secrets.stdout_lines %} + secrets: + - ssl_passphrase +{% endif %} + maintenance: + image: hub.andrewalliance.com/releases/nginx:1.29.5-alpine + volumes: + - ./maintenance.html:/usr/onelab/index.html + - ./maintenance.conf:/etc/nginx/conf.d/default.conf + deploy: + replicas: 1 + placement: + constraints: + - node.role == manager +{% if docker_secrets is defined and "ssl_passphrase" in docker_secrets.stdout_lines %} +secrets: + ssl_passphrase: + external: true +{% endif %} + diff --git a/app/proxy/error-404.html b/app/proxy/error-404.html new file mode 100644 index 0000000..2ffc0d8 --- /dev/null +++ b/app/proxy/error-404.html @@ -0,0 +1,98 @@ + + + + + + OneLab - Page Not Found + + + + + + + + +
+
+
+ OneLab logo +
+
Page not found
+
Error 404
+
The page you requested doesn’t exist, was removed, or is temporarily unavailable. You can return + to the application by logging in again.
+ +
+
+ + + \ No newline at end of file diff --git a/app/proxy/maintenance.conf b/app/proxy/maintenance.conf new file mode 100644 index 0000000..84bb425 --- /dev/null +++ b/app/proxy/maintenance.conf @@ -0,0 +1,9 @@ + +server { + listen 80; + location / { + root /usr/onelab; + index index.html index.htm; + try_files $uri $uri/ /index.html =404; + } +} diff --git a/app/proxy/maintenance.html b/app/proxy/maintenance.html new file mode 100644 index 0000000..ad5b45a --- /dev/null +++ b/app/proxy/maintenance.html @@ -0,0 +1,89 @@ + + + + + + + + OneLab - Maintenance + + + + + + + + + + + + + +
+
+
+ OneLab logo +
+
+ Maintenance +
+
We'll be back soon
+
OneLab is temporarily down for maintenance. + Thank you for your patience.
+
+
+ + + \ No newline at end of file diff --git a/app/proxy/not-supported.html b/app/proxy/not-supported.html new file mode 100644 index 0000000..29e4bc0 --- /dev/null +++ b/app/proxy/not-supported.html @@ -0,0 +1,82 @@ + + + + + + + + OneLab - Browser not supported + + + + + + + + + + + + + +
+
+
+ OneLab logo +
+
Your browser is not supported
+
OneLab works better on Chrome, Firefox and Microsoft Edge
+
+
+ + + \ No newline at end of file diff --git a/app/proxy/proxy.conf b/app/proxy/proxy.conf new file mode 100644 index 0000000..38a14bd --- /dev/null +++ b/app/proxy/proxy.conf @@ -0,0 +1,174 @@ +# Load the headers-more module +load_module /etc/nginx/modules/ngx_http_headers_more_filter_module.so; + +worker_processes 4; + +events { worker_connections 1024; } + +http { + server_names_hash_bucket_size 128; + include /etc/nginx/mime.types; + + types { + image/svg+xml svg svgz; + } + + client_max_body_size 600m; + + # don't send the nginx version number in error pages and Server header + server_tokens off; + more_set_headers 'Server: OneLab'; + + sendfile on; + + resolver 8.8.8.8; + + limit_req_zone $binary_remote_addr zone=proxy_global:10m rate=50r/s; + + upstream onelab { + server host.docker.internal:8080; + server maintenance:80 backup; + } + + # redirect all http traffic to https + server { + listen 80 default_server; +{% if (onelab.services.revproxy.ipv6|default(true)) != false %} + listen [::]:80 default_server; +{% endif %} + server_name {{ onelab.domain[8:] }}; + return 301 https://$host$request_uri; + } + + include /etc/nginx/custom-http.conf; + + server { + listen 443 ssl default_server; +{% if (onelab.services.revproxy.ipv6|default(true)) != false %} + listen [::]:443 ssl default_server; +{% endif %} + http2 on; + server_name {{ onelab.domain[8:] }}; + + ssl_certificate /etc/nginx/ssl/server.pem; + ssl_certificate_key /etc/nginx/ssl/server.key; +{% if docker_secrets is defined and "ssl_passphrase" in docker_secrets.stdout_lines %} + ssl_password_file /run/secrets/ssl_passphrase; +{% endif %} + # ssl_client_certificate /etc/nginx/ssl/ca.crt; + # ssl_verify_client off; + + # enable session resumption to improve https performance + # http://vincent.bernat.im/en/blog/2011-ssl-session-reuse-rfc5077.html + ssl_session_cache shared:SSL:50m; + ssl_session_timeout 1d; + ssl_session_tickets off; + + # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits + ssl_dhparam /etc/nginx/ssl/dhparam.pem; + + # enables server-side protection from BEAST attacks + # http://blog.ivanristic.com/2013/09/is-beast-still-a-threat.html + ssl_prefer_server_ciphers on; + # disable SSLv3(enabled by default since nginx 0.8.19) since it's less secure then TLS http://en.wikipedia.org/wiki/Secure_Sockets_Layer#SSL_3.0 + ssl_protocols TLSv1.2 TLSv1.3; + # ciphers chosen for forward secrecy and compatibility + # http://blog.ivanristic.com/2013/08/configuring-apache-nginx-and-openssl-for-forward-secrecy.html + ssl_ciphers 'TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256'; + ssl_ecdh_curve X25519:P-384:P-256; + + # enable ocsp stapling (mechanism by which a site can convey certificate revocation information to visitors in a privacy-preserving, scalable manner) + # http://blog.mozilla.org/security/2013/07/29/ocsp-stapling-in-firefox/ + resolver 8.8.8.8 8.8.4.4; + ssl_stapling on; + ssl_stapling_verify on; + ssl_trusted_certificate /etc/nginx/ssl/chain.pem; + + # read more here http://tautt.com/best-nginx-configuration-for-security/ + # config to enable HSTS(HTTP Strict Transport Security) https://developer.mozilla.org/en-US/docs/Security/HTTP_Strict_Transport_Security + # to avoid ssl stripping https://en.wikipedia.org/wiki/SSL_stripping#SSL_stripping + # also https://hstspreload.org/ + add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload"; + + # config to don't allow the browser to render the page inside an frame or iframe + # and avoid clickjacking http://en.wikipedia.org/wiki/Clickjacking + # if you need to allow [i]frames, you can use SAMEORIGIN or even set an uri with ALLOW-FROM uri + # https://developer.mozilla.org/en-US/docs/HTTP/X-Frame-Options + add_header X-Frame-Options SAMEORIGIN; + + # when serving user-supplied content, include a X-Content-Type-Options: nosniff header along with the Content-Type: header, + # to disable content-type sniffing on some browsers. + # https://www.owasp.org/index.php/List_of_useful_HTTP_headers + # currently suppoorted in IE > 8 http://blogs.msdn.com/b/ie/archive/2008/09/02/ie8-security-part-vi-beta-2-update.aspx + # http://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx + # 'soon' on Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=471020 + add_header X-Content-Type-Options nosniff; + + # This header enables the Cross-site scripting (XSS) filter built into most recent web browsers. + # It's usually enabled by default anyway, so the role of this header is to re-enable the filter for + # this particular website if it was disabled by the user. + # https://www.owasp.org/index.php/List_of_useful_HTTP_headers + add_header X-XSS-Protection "1; mode=block"; + + # with Content Security Policy (CSP) enabled(and a browser that supports it(http://caniuse.com/#feat=contentsecuritypolicy), + # you can tell the browser that it can only download content from the domains you explicitly allow + # http://www.html5rocks.com/en/tutorials/security/content-security-policy/ + # https://www.owasp.org/index.php/Content_Security_Policy + # I need to change our application code so we can increase security by disabling 'unsafe-inline' 'unsafe-eval' + # directives for css and js(if you have inline css or js, you will need to keep it too). + # more: http://www.html5rocks.com/en/tutorials/security/content-security-policy/#inline-code-considered-harmful + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://app.intercom.io https://widget.intercom.io https://js.intercomcdn.com https://*.google-analytics.com https://*.googletagmanager.com https://recaptcha.net https://*.recaptcha.net https://*.gstatic.com; img-src * data:; media-src *; style-src 'self' 'unsafe-inline' https://*.google-analytics.com https://*.googletagmanager.com https://recaptcha.net https://*.recaptcha.net https://*.gstatic.com; font-src 'self' https://*.intercomcdn.com https://*.google-analytics.com https://*.googletagmanager.com https://recaptcha.net https://*.recaptcha.net https://*.gstatic.com; frame-src *; connect-src 'self' https://*.intercom.io wss://*.intercom.io https://*.intercomcdn.com https://*.intercomcdn.eu https://*.intercomusercontent.com https://*.intercom-messenger.com wss://*.intercom-messenger.com https://*.google-analytics.com https://*.googletagmanager.com https://recaptcha.net https://*.recaptcha.net https://*.gstatic.com; object-src 'none'"; + + add_header Referrer-Policy "same-origin"; + + add_header Permissions-Policy "accelerometer=(), autoplay=(), camera=(), encrypted-media=(), fullscreen=(self), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), sync-xhr=(self), usb=()"; + + gzip on; + gzip_vary on; + gzip_min_length 10240; + gzip_proxied expired no-cache no-store private auth; + gzip_types text/plain text/css application/json application/x-javascript application/javascript text/xml application/xml application/xml+rss text/javascript; + gzip_disable "MSIE [1-6]\."; + + root /usr/onelab/; + + include /etc/nginx/custom-server.conf; + + location /not-supported { + index not-supported.html not-supported.htm; + try_files $uri $uri/ /not-supported.html =404; + } + + location /error-404 { + index error-404.html error-404.htm; + try_files $uri $uri/ /error-404.html =404; + } + + # Also used as workaround for a Docker swarm issue + # https://github.com/moby/moby/issues/25526 + # Redirect to HTTP, but assume that we will be on the same server + location / { + limit_req zone=proxy_global burst=50 nodelay; + + if ($http_user_agent ~* '(MSIE 11.0|MSIE 10.0|MSIE 9.0|MSIE 8.0|MSIE 7.0|MSIE 6.0)') { + return 301 https://$host/not-supported; + } + + proxy_pass http://onelab; + proxy_next_upstream error; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-Proto $scheme; + + # WebSocket support + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + } +} + diff --git a/app/rabbit/advanced.conf b/app/rabbit/advanced.conf new file mode 100644 index 0000000..dcbcaf6 --- /dev/null +++ b/app/rabbit/advanced.conf @@ -0,0 +1,5 @@ +[ + {rabbit, [ + {tcp_listeners, []} + ]} +]. diff --git a/app/rabbit/definitions.json b/app/rabbit/definitions.json new file mode 100644 index 0000000..52c0e0a --- /dev/null +++ b/app/rabbit/definitions.json @@ -0,0 +1,20 @@ +{ + "rabbit_version": "3.6.5", + "users": [ + ], + "vhosts": [ + { + "name": "devices" + }, + { + "name": "internal" + } + ], + "permissions": [ + ], + "parameters": [], + "policies": [], + "queues": [], + "exchanges": [], + "bindings": [] +} \ No newline at end of file diff --git a/app/rabbit/enable_plugins b/app/rabbit/enable_plugins new file mode 100644 index 0000000..64e1fe1 --- /dev/null +++ b/app/rabbit/enable_plugins @@ -0,0 +1 @@ +[rabbitmq_auth_backend_http, rabbitmq_auth_backend_cache, rabbitmq_management, rabbitmq_event_exchange]. \ No newline at end of file diff --git a/app/rabbit/rabbit.conf b/app/rabbit/rabbit.conf new file mode 100644 index 0000000..ce56423 --- /dev/null +++ b/app/rabbit/rabbit.conf @@ -0,0 +1,54 @@ +# Auth server config + +auth_backends.1 = cache + +auth_cache.cached_backend = http +auth_cache.cache_ttl = 5000 + +auth_http.user_path = http://revproxy/api/v1/rmq/user +auth_http.vhost_path = http://revproxy/api/v1/rmq/vhost +auth_http.resource_path = http://revproxy/api/v1/rmq/resource +auth_http.topic_path = http://revproxy/api/v1/rmq/topic + +listeners.ssl.default = 5671 + +# generated with "cat server.crt server.key > server.pem" +# fullchain +ssl_options.cacertfile = /etc/rabbitmq/ssl/rabbit.fullchain.pem + +ssl_options.certfile = /etc/rabbitmq/ssl/rabbit.crt +ssl_options.keyfile = /etc/rabbitmq/ssl/rabbit.key + +# not very secure +ssl_options.verify = verify_peer +ssl_options.fail_if_no_peer_cert = false + +# for TLS version and cipher +ssl_options.versions.1 = tlsv1.2 + +# these MUST be disabled if TLSv1.3 is used +ssl_options.honor_cipher_order = true +ssl_options.honor_ecc_order = true + +# These are highly recommended for TLSv1.2 but cannot be used +# with TLSv1.3. If TLSv1.3 is enabled, these lines MUST be removed. +ssl_options.client_renegotiation = false +ssl_options.secure_renegotiate = true + +ssl_options.ciphers.1 = ECDHE-ECDSA-AES256-GCM-SHA384 +ssl_options.ciphers.2 = ECDHE-RSA-AES256-GCM-SHA384 +ssl_options.ciphers.3 = ECDH-ECDSA-AES256-GCM-SHA384 +ssl_options.ciphers.4 = ECDH-RSA-AES256-GCM-SHA384 +ssl_options.ciphers.5 = DHE-RSA-AES256-GCM-SHA384 +ssl_options.ciphers.6 = DHE-DSS-AES256-GCM-SHA384 +ssl_options.ciphers.7 = ECDHE-ECDSA-AES128-GCM-SHA256 +ssl_options.ciphers.8 = ECDHE-RSA-AES128-GCM-SHA256 +ssl_options.ciphers.9 = ECDH-ECDSA-AES128-GCM-SHA256 +ssl_options.ciphers.10 = ECDH-RSA-AES128-GCM-SHA256 +ssl_options.ciphers.11 = DHE-RSA-AES128-GCM-SHA256 +ssl_options.ciphers.12 = DHE-DSS-AES128-GCM-SHA256 + +management.load_definitions = /opt/definitions.json + +# For connection events +event_exchange.vhost = devices \ No newline at end of file diff --git a/app/rabbit/ssl/rabbit.crt b/app/rabbit/ssl/rabbit.crt new file mode 100644 index 0000000..f1c446a --- /dev/null +++ b/app/rabbit/ssl/rabbit.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDwTCCAqmgAwIBAgIJANBW7LFskUGkMA0GCSqGSIb3DQEBCwUAMHcxCzAJBgNV +BAYTAkNIMQ8wDQYDVQQIDAZHZW5ldmExDzANBgNVBAcMBkdlbmV2YTEYMBYGA1UE +CgwPQW5kcmV3IEFsbGlhbmNlMQ8wDQYDVQQLDAZPbmVMYWIxGzAZBgNVBAMMEmFu +ZHJld2FsbGlhbmNlLmNvbTAeFw0xOTA5MzAxNTE5NDJaFw0zOTA5MjUxNTE5NDJa +MHcxCzAJBgNVBAYTAkNIMQ8wDQYDVQQIDAZHZW5ldmExDzANBgNVBAcMBkdlbmV2 +YTEYMBYGA1UECgwPQW5kcmV3IEFsbGlhbmNlMQ8wDQYDVQQLDAZPbmVMYWIxGzAZ +BgNVBAMMEmFuZHJld2FsbGlhbmNlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAMxODu2ql2+397wUYVRjuNppa2+nLZwSvzsnyfG5RwmQX/Q3V9sy +17RluFW42WLFv5TfS/soRILbShyHmYJL9iGfsa1nkg9XAs681ebHESrGP9jZZ25x +alg97IaVI6rQcUN/7WrB183sAokL4AinY2Zh+wOt9LQWsovKO3TB3Oetxw3AImqX +onuzTZkzlbJ0KTHGpn8dZ/xjGMvkd1ByjKLQ/gb630vZOJErjcLEWji20fWhwA5S +7vhGy4dqivEpLrnEjd2njclADphOxYoHr2rN11n7ASl1+Lu5HhCAFNoeaDi+rliu +pupKcbFYCPLIpTq2yw81BOkKn0fPMn5C+m0CAwEAAaNQME4wHQYDVR0OBBYEFGYz +XZCPfN8IfCDjPVS0NUcK+c6zMB8GA1UdIwQYMBaAFGYzXZCPfN8IfCDjPVS0NUcK ++c6zMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAABxoVD1aZ8NJU2+ +cL8lMFfrrQEg3vSNzYWuI78OHixGYDF3NPARFL6m20hLusxtmtsM+OsWios58RoO +0b9unY3ce0S/567LFpBZgKvo7hdEwDBY2cuX5p8snWfucn4j8KZhebtz4kvcSPOH +VTWtgfobKxcryiM8pYA8uRzu/WQKr2q92oYliiOoUg6ZQEWhBCqXzcHJ7D32UcuW +fHfZCp48D9LqfSXALNOHyXWaLXRIiQHBuWMhBI57tgbPA4m2hHN8c8y1rc0cSNGt +Og8tQoRlj9PRM3WMMrdGuG4TvBe3y5666wo1ZLErfyCbM4/sIPHdrp1cCXFDyyVj +nKZ2gjE= +-----END CERTIFICATE----- diff --git a/app/rabbit/ssl/rabbit.fullchain.pem b/app/rabbit/ssl/rabbit.fullchain.pem new file mode 100644 index 0000000..f0872bf --- /dev/null +++ b/app/rabbit/ssl/rabbit.fullchain.pem @@ -0,0 +1,51 @@ +-----BEGIN CERTIFICATE----- +MIIDwTCCAqmgAwIBAgIJANBW7LFskUGkMA0GCSqGSIb3DQEBCwUAMHcxCzAJBgNV +BAYTAkNIMQ8wDQYDVQQIDAZHZW5ldmExDzANBgNVBAcMBkdlbmV2YTEYMBYGA1UE +CgwPQW5kcmV3IEFsbGlhbmNlMQ8wDQYDVQQLDAZPbmVMYWIxGzAZBgNVBAMMEmFu +ZHJld2FsbGlhbmNlLmNvbTAeFw0xOTA5MzAxNTE5NDJaFw0zOTA5MjUxNTE5NDJa +MHcxCzAJBgNVBAYTAkNIMQ8wDQYDVQQIDAZHZW5ldmExDzANBgNVBAcMBkdlbmV2 +YTEYMBYGA1UECgwPQW5kcmV3IEFsbGlhbmNlMQ8wDQYDVQQLDAZPbmVMYWIxGzAZ +BgNVBAMMEmFuZHJld2FsbGlhbmNlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAMxODu2ql2+397wUYVRjuNppa2+nLZwSvzsnyfG5RwmQX/Q3V9sy +17RluFW42WLFv5TfS/soRILbShyHmYJL9iGfsa1nkg9XAs681ebHESrGP9jZZ25x +alg97IaVI6rQcUN/7WrB183sAokL4AinY2Zh+wOt9LQWsovKO3TB3Oetxw3AImqX +onuzTZkzlbJ0KTHGpn8dZ/xjGMvkd1ByjKLQ/gb630vZOJErjcLEWji20fWhwA5S +7vhGy4dqivEpLrnEjd2njclADphOxYoHr2rN11n7ASl1+Lu5HhCAFNoeaDi+rliu +pupKcbFYCPLIpTq2yw81BOkKn0fPMn5C+m0CAwEAAaNQME4wHQYDVR0OBBYEFGYz +XZCPfN8IfCDjPVS0NUcK+c6zMB8GA1UdIwQYMBaAFGYzXZCPfN8IfCDjPVS0NUcK ++c6zMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAABxoVD1aZ8NJU2+ +cL8lMFfrrQEg3vSNzYWuI78OHixGYDF3NPARFL6m20hLusxtmtsM+OsWios58RoO +0b9unY3ce0S/567LFpBZgKvo7hdEwDBY2cuX5p8snWfucn4j8KZhebtz4kvcSPOH +VTWtgfobKxcryiM8pYA8uRzu/WQKr2q92oYliiOoUg6ZQEWhBCqXzcHJ7D32UcuW +fHfZCp48D9LqfSXALNOHyXWaLXRIiQHBuWMhBI57tgbPA4m2hHN8c8y1rc0cSNGt +Og8tQoRlj9PRM3WMMrdGuG4TvBe3y5666wo1ZLErfyCbM4/sIPHdrp1cCXFDyyVj +nKZ2gjE= +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDMTg7tqpdvt/e8 +FGFUY7jaaWtvpy2cEr87J8nxuUcJkF/0N1fbMte0ZbhVuNlixb+U30v7KESC20oc +h5mCS/Yhn7GtZ5IPVwLOvNXmxxEqxj/Y2WducWpYPeyGlSOq0HFDf+1qwdfN7AKJ +C+AIp2NmYfsDrfS0FrKLyjt0wdznrccNwCJql6J7s02ZM5WydCkxxqZ/HWf8YxjL +5HdQcoyi0P4G+t9L2TiRK43CxFo4ttH1ocAOUu74RsuHaorxKS65xI3dp43JQA6Y +TsWKB69qzddZ+wEpdfi7uR4QgBTaHmg4vq5YrqbqSnGxWAjyyKU6tssPNQTpCp9H +zzJ+QvptAgMBAAECggEBAMNTqcAeBdSgvTBLB9bH1Ja0jSvdWKTL45qp6s+5BKqn +JqX1N4DxNftZ+Qezx9pAF288oYcJM58sCsoF9oT1HOd+pz+TMriJxrxjG9oc3lPX +4HjxOYsE0dh1s6nj8oq/6FnRd+pGU42jo7Tk7pc3ebgL2xHkgXyf6qntejShmBLC +gbXM86sPp1GYFNrVh6JveL8UBQzsXnY1u8tm4J2xA47+UvF0uPV5b7xzblplqycv +K/cijsqiaBhDZR71Nj7/HnHqcJqs4pYjnNplkfJ5fjkifJh8xZcX0vdJnc7hgC/x +BLFuMVLUSXoWtBmajVoJaD/46b0pFTEeGmXs6h3TMj0CgYEA+26WRyiyDGl3wgYJ +dm9ITuwBZQo1HiA8wGgAEqyM8BcxNh7zGhYTBKPUYJtuJsUZcFCAbXlD5nIDOYeR +bODqEgtN6RQPtQHU4My1gtd8Aaa2IlVD+GVYFSldpcud0pswbiE0xA3GcknGM3hf +Jh/7pDEYoN0DkSgyVwL5+d/irVcCgYEA0ARIZC/NyhHC17FGXF5qIG45b+pyvpAE ++ytjRHZ09WPErONnD29Q7dhNaYqi6ZtLS1XuG7GVo5VnV+xdr75e93sQuyXzAKnx +f1zn+qEDDY/K0qPeh96sHxJjwE6Ha5mO/kL7mx1yV9QEPHTNEynZMM1n2ahgv0kW +LM3vymDdN9sCgYAxCrX3NUHdV+kLCEBqQHR7KF9xRNdtg941rVNuUQfAgNWRd+H1 +EZ8uBYKUQVzv5Pv8Q0d5kvCij1R/sSi1cv1U/a15q/cQWEYcfvFcjwlaDRlzguwa +FDJlFkd04k1rWefNY162avVtL2Sgf3cXqZ9cvFLQdauPcaa4ABWgHoJJSwKBgQCF +3JuxTUoC5Vcbcosy2dc1s3jm2lqo4wkxkQQ6hHhTibaBA33I4vuTc4StjHe2GSdk +/ZFS5P7E3Fo6rHLFq551aA6mQI+WN8afDWyDTqYmTghZcPa9PMBK5oymrmJbDeCF +IxnDAjoY5ukq9ocZ8+4+h5By8vX/YssQXh4XgCobzQKBgDMgav9ABc0idoE8sQ0g +bnPI/QX+bRhm+yYUQM/zB0h/CRuUC44ZP7dssST7c4L/OirYiZQEtXnHdxZ2DmV3 +v/5MNbLnx+1z75KLwtg4f24BRB6p26/YxN1Wtudu7QwSSjffGRcUucbS5X3//Fjb +3YIcbL9y2FZJHQFybilFQJgu +-----END PRIVATE KEY----- diff --git a/app/rabbit/ssl/rabbit.key b/app/rabbit/ssl/rabbit.key new file mode 100644 index 0000000..1cd0dee --- /dev/null +++ b/app/rabbit/ssl/rabbit.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDMTg7tqpdvt/e8 +FGFUY7jaaWtvpy2cEr87J8nxuUcJkF/0N1fbMte0ZbhVuNlixb+U30v7KESC20oc +h5mCS/Yhn7GtZ5IPVwLOvNXmxxEqxj/Y2WducWpYPeyGlSOq0HFDf+1qwdfN7AKJ +C+AIp2NmYfsDrfS0FrKLyjt0wdznrccNwCJql6J7s02ZM5WydCkxxqZ/HWf8YxjL +5HdQcoyi0P4G+t9L2TiRK43CxFo4ttH1ocAOUu74RsuHaorxKS65xI3dp43JQA6Y +TsWKB69qzddZ+wEpdfi7uR4QgBTaHmg4vq5YrqbqSnGxWAjyyKU6tssPNQTpCp9H +zzJ+QvptAgMBAAECggEBAMNTqcAeBdSgvTBLB9bH1Ja0jSvdWKTL45qp6s+5BKqn +JqX1N4DxNftZ+Qezx9pAF288oYcJM58sCsoF9oT1HOd+pz+TMriJxrxjG9oc3lPX +4HjxOYsE0dh1s6nj8oq/6FnRd+pGU42jo7Tk7pc3ebgL2xHkgXyf6qntejShmBLC +gbXM86sPp1GYFNrVh6JveL8UBQzsXnY1u8tm4J2xA47+UvF0uPV5b7xzblplqycv +K/cijsqiaBhDZR71Nj7/HnHqcJqs4pYjnNplkfJ5fjkifJh8xZcX0vdJnc7hgC/x +BLFuMVLUSXoWtBmajVoJaD/46b0pFTEeGmXs6h3TMj0CgYEA+26WRyiyDGl3wgYJ +dm9ITuwBZQo1HiA8wGgAEqyM8BcxNh7zGhYTBKPUYJtuJsUZcFCAbXlD5nIDOYeR +bODqEgtN6RQPtQHU4My1gtd8Aaa2IlVD+GVYFSldpcud0pswbiE0xA3GcknGM3hf +Jh/7pDEYoN0DkSgyVwL5+d/irVcCgYEA0ARIZC/NyhHC17FGXF5qIG45b+pyvpAE ++ytjRHZ09WPErONnD29Q7dhNaYqi6ZtLS1XuG7GVo5VnV+xdr75e93sQuyXzAKnx +f1zn+qEDDY/K0qPeh96sHxJjwE6Ha5mO/kL7mx1yV9QEPHTNEynZMM1n2ahgv0kW +LM3vymDdN9sCgYAxCrX3NUHdV+kLCEBqQHR7KF9xRNdtg941rVNuUQfAgNWRd+H1 +EZ8uBYKUQVzv5Pv8Q0d5kvCij1R/sSi1cv1U/a15q/cQWEYcfvFcjwlaDRlzguwa +FDJlFkd04k1rWefNY162avVtL2Sgf3cXqZ9cvFLQdauPcaa4ABWgHoJJSwKBgQCF +3JuxTUoC5Vcbcosy2dc1s3jm2lqo4wkxkQQ6hHhTibaBA33I4vuTc4StjHe2GSdk +/ZFS5P7E3Fo6rHLFq551aA6mQI+WN8afDWyDTqYmTghZcPa9PMBK5oymrmJbDeCF +IxnDAjoY5ukq9ocZ8+4+h5By8vX/YssQXh4XgCobzQKBgDMgav9ABc0idoE8sQ0g +bnPI/QX+bRhm+yYUQM/zB0h/CRuUC44ZP7dssST7c4L/OirYiZQEtXnHdxZ2DmV3 +v/5MNbLnx+1z75KLwtg4f24BRB6p26/YxN1Wtudu7QwSSjffGRcUucbS5X3//Fjb +3YIcbL9y2FZJHQFybilFQJgu +-----END PRIVATE KEY----- diff --git a/app/reconfigure.sh b/app/reconfigure.sh new file mode 100644 index 0000000..2aac25f --- /dev/null +++ b/app/reconfigure.sh @@ -0,0 +1,21 @@ + +#!/bin/bash +# Usage: ./reconfigure.sh [--no-pull] + +onelab_no_pull="" + +for arg in "$@"; do + if [[ "$arg" == "--no-pull" ]]; then + onelab_no_pull="no-pull" + fi +done + +LOG_PATH="./logs/ansible/$(date +%F)-ansible.log" +PLAYBOOK="./installation/1.27.0/app/playbooks/reconfigure.yml" +EXTRA_VARS=() + +if [[ -n "$onelab_no_pull" ]]; then + EXTRA_VARS+=("-e" "policy=$onelab_no_pull") +fi + +ANSIBLE_LOG_PATH="$LOG_PATH" ansible-playbook "$PLAYBOOK" "${EXTRA_VARS[@]}" \ No newline at end of file diff --git a/app/start.sh b/app/start.sh new file mode 100644 index 0000000..9098003 --- /dev/null +++ b/app/start.sh @@ -0,0 +1,3 @@ + +#!/bin/bash +ANSIBLE_LOG_PATH=./logs/ansible/$(date +%F)-ansible.log ansible-playbook ./installation/latest/app/playbooks/deploy.yml \ No newline at end of file diff --git a/app/stop.sh b/app/stop.sh new file mode 100644 index 0000000..9704fb9 --- /dev/null +++ b/app/stop.sh @@ -0,0 +1,3 @@ + +#!/bin/bash +ANSIBLE_LOG_PATH=./logs/ansible/$(date +%F)-ansible.log ansible-playbook ./installation/latest/app/playbooks/stop.yml \ No newline at end of file diff --git a/app/uninstall.sh b/app/uninstall.sh new file mode 100644 index 0000000..8e3df1e --- /dev/null +++ b/app/uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/bash +ansible-playbook ./installation/1.27.0/app/playbooks/uninstall.yml \ No newline at end of file diff --git a/app/update.sh b/app/update.sh new file mode 100644 index 0000000..dca5e40 --- /dev/null +++ b/app/update.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# Usage: ./update.sh [version] [--force] + +onelab_version="" +onelab_force_update="" + +for arg in "$@"; do + if [[ "$arg" == "--force" ]]; then + onelab_force_update="--force" + else + onelab_version="$arg" + fi +done + +LOG_PATH="./logs/ansible/$(date +%F)-ansible.log" +PLAYBOOK="./installation/1.27.0/app/playbooks/update.yml" +EXTRA_VARS=() + +if [[ -n "$onelab_version" ]]; then + EXTRA_VARS+=("-e" "version=$onelab_version") +fi +if [[ -n "$onelab_force_update" ]]; then + EXTRA_VARS+=("-e" "userchoice=yes") +fi + +ANSIBLE_LOG_PATH="$LOG_PATH" ansible-playbook "$PLAYBOOK" "${EXTRA_VARS[@]}" diff --git a/gitops/README.md b/gitops/README.md new file mode 100644 index 0000000..30a32ac --- /dev/null +++ b/gitops/README.md @@ -0,0 +1,52 @@ +# OneLab GitOps (k3s + Argo CD) + +This directory holds the **Helm chart** that replaces `docker stack deploy` from the legacy Swarm installer (`app/docker-compose.yml`). + +## Layout + +| Path | Purpose | +|------|---------| +| `charts/onelab` | Helm chart (StatefulSets, Deployments, Services, ConfigMaps, Secrets) | +| `values/*.yaml` | Environment-specific overrides (non-secret defaults; use sealed/external secrets for prod) | +| `argocd/application.yaml` | Example `Application` — set `repoURL` / `targetRevision` to your remote | + +## Prerequisites + +1. **k3s** (or any Kubernetes) with default storage class for Postgres/Rabbit PVCs (e.g. `local-path`). +2. **Image pull access** to `hub.andrewalliance.com` — create a docker-registry secret and reference it in `imagePullSecrets`: + ```bash + kubectl create namespace onelab + kubectl create secret docker-registry hub-andrewalliance -n onelab \ + --docker-server=hub.andrewalliance.com --docker-username=... --docker-password=... + ``` +3. **RabbitMQ TLS secret** (name `onelab-rabbit-tls` by default) — see `values/k3s-example.yaml` comments, or set `rabbitmq.tls.embed: true` with PEM strings in a **private** values file. +4. **Host paths** (default): ensure `/opt/onelab/data` and `/opt/onelab/logs` exist on nodes that run workloads using `persistence.mode: hostPath`, or switch to RWX storage for multi-node. + +## Helm (without Argo CD) + +```bash +cd gitops/charts/onelab +helm upgrade --install onelab . -n onelab --create-namespace \ + -f ../../values/k3s-example.yaml +``` + +## Argo CD + +1. Push this repository to a Git remote Argo CD can read. +2. Edit `argocd/application.yaml`: `repoURL`, `targetRevision`, and values file as needed. +3. `kubectl apply -f gitops/argocd/application.yaml` (from a machine with a working kubeconfig). + +Sync waves order Postgres → Redis/Rabbit/config → application pods. + +## kubectl / credentials + +If `kubectl` reports *You must be logged in*, refresh your kubeconfig (e.g. copy `/etc/rancher/k3s/k3s.yaml` from the server or re-run your auth plugin) before applying manifests. + +## Helm note (Windows) + +Helm 3.19 may return empty content for `.Files.Get` on Windows; this chart uses `fromYaml (.Files.AsConfig)` as a workaround so packaged files still render correctly. + +## Not migrated in this chart + +- **Edge proxy stack** (`app/proxy/docker-compose.yml`, host 80/443) — use k3s **Traefik** / **Ingress** + **cert-manager**, or a separate DaemonSet/nginx chart. +- **Swarm-only secrets** (e.g. `ssl_passphrase`) — handle via Kubernetes Secrets or external operators. diff --git a/gitops/argocd/application.yaml b/gitops/argocd/application.yaml new file mode 100644 index 0000000..50b5ae7 --- /dev/null +++ b/gitops/argocd/application.yaml @@ -0,0 +1,26 @@ +# Syncs chart from Git; ensure Argo CD can clone repoURL (add credentials in Argo if private). +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: onelab + namespace: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + source: + repoURL: https://git.luneski.fr/luneski/onelab-k8s.git + targetRevision: main + path: gitops/charts/onelab + helm: + valueFiles: + - ../../values/k3s-example.yaml + destination: + server: https://kubernetes.default.svc + namespace: onelab + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/gitops/charts/onelab/Chart.yaml b/gitops/charts/onelab/Chart.yaml new file mode 100644 index 0000000..02e095d --- /dev/null +++ b/gitops/charts/onelab/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: onelab +description: OneLab stack for Kubernetes (migrated from Docker Swarm compose) +type: application +version: 0.1.0 +appVersion: "1.27.0" diff --git a/gitops/charts/onelab/files/advanced.conf b/gitops/charts/onelab/files/advanced.conf new file mode 100644 index 0000000..dcbcaf6 --- /dev/null +++ b/gitops/charts/onelab/files/advanced.conf @@ -0,0 +1,5 @@ +[ + {rabbit, [ + {tcp_listeners, []} + ]} +]. diff --git a/gitops/charts/onelab/files/configurations.gotmpl b/gitops/charts/onelab/files/configurations.gotmpl new file mode 100644 index 0000000..3235418 --- /dev/null +++ b/gitops/charts/onelab/files/configurations.gotmpl @@ -0,0 +1,91 @@ +--- +onelab: + domain: {{ .Values.onelab.domain | quote }} + logs: + level: info + assets: + purge: 1d + shared: + inputs: + path: "./data/shared/inputs" + archive_path: "./data/shared/archived" + security: + cors: '*' + auth: + token: + expiration: 5m + key: {{ .Values.onelab.secrets.authTokenKey | quote }} + password: + expiration: 90d + min_length: 8 + prevent_reuse: 5 + allow_list: [] + block_list: [] + authentifier: "email" + ratelimit: + ip: + max: 1000 + duration: 1d + auth: + max: 5 + duration: 5m + delay_after: 2 + delay_ms: 1000 + devices: + cors: '*' + monitoring: + token: {{ .Values.onelab.secrets.monitoringToken | quote }} + params: + session: + idle: 45m + remember_me: true + lab: + creation_policy: many + signup: false +{{- if .Values.onelab.intercom.appid }} + intercom: + appid: {{ .Values.onelab.intercom.appid | quote }} + secret: {{ .Values.onelab.intercom.secret | quote }} +{{- end }} + mailer: + noreply: {{ .Values.onelab.mailer.noreply | quote }} + queue: + scheduling: 15 + maxsize: 50 + error: + maxtries: 3 + timeout: 60 + ldap: + enabled: {{ .Values.features.ldapWorker }} + services: + db: + host: db + database: postgres + username: postgres + password: {{ .Values.postgresql.auth.password | quote }} + schema: onelab + redis: + host: redis + port: "6379" + rabbit: + url: rabbitmq + port: 5671 + token: {{ .Values.onelab.secrets.rabbitToken | quote }} + api: + replicas: {{ .Values.replicas.api }} + apidevice: + replicas: {{ .Values.replicas.apidevice }} + apirabbit: + replicas: {{ .Values.replicas.apirabbit }} + devices: + replicas: {{ .Values.replicas.devices }} + experiments: + replicas: {{ .Values.replicas.experiments }} + images: + replicas: {{ .Values.replicas.images }} + manual: + replicas: {{ .Values.replicas.manual }} + website: + ssr: {{ .Values.website.ssr }} + ws: + replicas: {{ .Values.replicas.ws }} diff --git a/gitops/charts/onelab/files/enable_plugins b/gitops/charts/onelab/files/enable_plugins new file mode 100644 index 0000000..64e1fe1 --- /dev/null +++ b/gitops/charts/onelab/files/enable_plugins @@ -0,0 +1 @@ +[rabbitmq_auth_backend_http, rabbitmq_auth_backend_cache, rabbitmq_management, rabbitmq_event_exchange]. \ No newline at end of file diff --git a/gitops/charts/onelab/files/error-404.html b/gitops/charts/onelab/files/error-404.html new file mode 100644 index 0000000..2ffc0d8 --- /dev/null +++ b/gitops/charts/onelab/files/error-404.html @@ -0,0 +1,98 @@ + + + + + + OneLab - Page Not Found + + + + + + + + +
+
+
+ OneLab logo +
+
Page not found
+
Error 404
+
The page you requested doesn’t exist, was removed, or is temporarily unavailable. You can return + to the application by logging in again.
+ +
+
+ + + \ No newline at end of file diff --git a/gitops/charts/onelab/files/nginx.conf.tpl b/gitops/charts/onelab/files/nginx.conf.tpl new file mode 100644 index 0000000..29da65c --- /dev/null +++ b/gitops/charts/onelab/files/nginx.conf.tpl @@ -0,0 +1,337 @@ + +worker_processes 4; + +events { worker_connections 1024; } + +http { + include /etc/nginx/mime.types; + + client_max_body_size 2m; + + # don't send the nginx version number in error pages and Server header + server_tokens off; + + sendfile on; + + upstream website { + server website:4000; + } + + upstream main { + server main:80; + } + + upstream api { + server api:3000; + } + + upstream api-device { + server api-device:3000; + } + + upstream api-rabbit { + server api-rabbit:3000; + } + + upstream designer { + server designer:80; + } + + upstream runner { + server runner:80; + } + + upstream static { + server static:80; + } + + upstream websocket-worker { + server websocket-worker:3030; + } + + # Restore the real client IP from the upstream reverse proxy + # Trust all RFC 1918 private ranges (covers any Docker network config) + set_real_ip_from 10.0.0.0/8; + set_real_ip_from 172.16.0.0/12; + set_real_ip_from 192.168.0.0/16; + real_ip_header X-Real-IP; + + limit_req_zone $binary_remote_addr zone=auth:10m rate=1r/s; + limit_req_zone $binary_remote_addr zone=website:10m rate=5r/s; + limit_req_zone $binary_remote_addr zone=global:10m rate=10r/s; + + # redirect all http traffic to https + server { + listen 80 default_server; +{{- if .Values.revproxy.ipv6Listen }} + listen [::]:80 default_server; +{{- end }} + server_name localhost; + + gzip on; + gzip_vary on; + gzip_min_length 10240; + gzip_proxied expired no-cache no-store private auth; + gzip_types text/plain text/css application/json application/x-javascript application/javascript text/xml application/xml application/xml+rss text/javascript; + gzip_disable "MSIE [1-6]\."; + + root /data/; + + location ^~ /lab/ { + rewrite ^/lab/(.*?) /app/lab/$1 last; + } + + location ^~ /assets/ { + location ~* \.(?:css|js|woff|woff2|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ { + expires 1M; + access_log off; + add_header Cache-Control "public"; + + try_files $uri $uri/ /error-404.html =404; + } + expires -1; + add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; + + try_files $uri $uri/ /error-404.html =404; + } + + location ^~ /static/ { + location ~* \.(?:css|js|woff|woff2|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ { + expires 1M; + access_log off; + add_header Cache-Control "public"; + + proxy_pass http://static; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + expires -1; + add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; + + proxy_pass http://static; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location ^~ /sitemap.xml { + limit_req zone=global burst=10 nodelay; + + proxy_pass http://api; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location ^~ /robots.txt { + limit_req zone=global burst=10 nodelay; + + proxy_pass http://api; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location ^~ /api/ { + limit_req zone=global burst=10 nodelay; + + proxy_pass http://api; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location ^~ /api/v1/auth/ { + # apply rate limiting + limit_req zone=auth burst=5 nodelay; + + proxy_pass http://api; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location ^~ /api/v1/firmwares { + limit_req zone=global burst=10 nodelay; + + client_max_body_size 600M; + + proxy_pass http://api; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location ^~ /api/v1/rmq/ { + proxy_pass http://api-rabbit; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location ^~ /device-api/ { + limit_req zone=global burst=10 nodelay; + client_max_body_size 600m; + + proxy_pass http://api-device; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location ^~ /app/designer/ { + location ~* \.(?:css|js|woff|woff2|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ { + expires 1M; + access_log off; + add_header Cache-Control "public"; + + proxy_pass http://designer; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + expires -1; + add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; + + proxy_pass http://designer; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location ^~ /app/runner/ { + location ~* \.(?:css|js|woff|woff2|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ { + expires 1M; + access_log off; + add_header Cache-Control "public"; + + proxy_pass http://runner; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + expires -1; + add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; + + proxy_pass http://runner; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location ^~ /ws/ { + proxy_pass http://websocket-worker; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + location ^~ /app/ { + location ~* \.(?:css|js|woff|woff2|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ { + expires 1M; + access_log off; + add_header Cache-Control "public"; + + proxy_pass http://main; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + expires -1; + add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; + + proxy_pass http://main; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + # Static assets for website (including /media/ subdirectory) + location ~* ^/.+\.(?:css|js|woff|woff2|ttf|eot|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ { + expires 1M; + access_log off; + add_header Cache-Control "public"; + + proxy_pass http://website; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + # Website SSR routes (rate limited) + location ~ ^/(library(/[^/]+)?|login(/(reset|change))?|signup)?$ { + limit_req zone=website burst=10 nodelay; + + expires -1; + add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; + + proxy_pass http://website; + proxy_redirect off; + proxy_set_header Cookie $http_cookie; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + # Catch-all for junk requests + location / { + try_files $uri $uri/ /error-404.html =404; + } + } +} diff --git a/gitops/charts/onelab/files/rabbit-definitions.json b/gitops/charts/onelab/files/rabbit-definitions.json new file mode 100644 index 0000000..52c0e0a --- /dev/null +++ b/gitops/charts/onelab/files/rabbit-definitions.json @@ -0,0 +1,20 @@ +{ + "rabbit_version": "3.6.5", + "users": [ + ], + "vhosts": [ + { + "name": "devices" + }, + { + "name": "internal" + } + ], + "permissions": [ + ], + "parameters": [], + "policies": [], + "queues": [], + "exchanges": [], + "bindings": [] +} \ No newline at end of file diff --git a/gitops/charts/onelab/files/rabbit.conf b/gitops/charts/onelab/files/rabbit.conf new file mode 100644 index 0000000..ce56423 --- /dev/null +++ b/gitops/charts/onelab/files/rabbit.conf @@ -0,0 +1,54 @@ +# Auth server config + +auth_backends.1 = cache + +auth_cache.cached_backend = http +auth_cache.cache_ttl = 5000 + +auth_http.user_path = http://revproxy/api/v1/rmq/user +auth_http.vhost_path = http://revproxy/api/v1/rmq/vhost +auth_http.resource_path = http://revproxy/api/v1/rmq/resource +auth_http.topic_path = http://revproxy/api/v1/rmq/topic + +listeners.ssl.default = 5671 + +# generated with "cat server.crt server.key > server.pem" +# fullchain +ssl_options.cacertfile = /etc/rabbitmq/ssl/rabbit.fullchain.pem + +ssl_options.certfile = /etc/rabbitmq/ssl/rabbit.crt +ssl_options.keyfile = /etc/rabbitmq/ssl/rabbit.key + +# not very secure +ssl_options.verify = verify_peer +ssl_options.fail_if_no_peer_cert = false + +# for TLS version and cipher +ssl_options.versions.1 = tlsv1.2 + +# these MUST be disabled if TLSv1.3 is used +ssl_options.honor_cipher_order = true +ssl_options.honor_ecc_order = true + +# These are highly recommended for TLSv1.2 but cannot be used +# with TLSv1.3. If TLSv1.3 is enabled, these lines MUST be removed. +ssl_options.client_renegotiation = false +ssl_options.secure_renegotiate = true + +ssl_options.ciphers.1 = ECDHE-ECDSA-AES256-GCM-SHA384 +ssl_options.ciphers.2 = ECDHE-RSA-AES256-GCM-SHA384 +ssl_options.ciphers.3 = ECDH-ECDSA-AES256-GCM-SHA384 +ssl_options.ciphers.4 = ECDH-RSA-AES256-GCM-SHA384 +ssl_options.ciphers.5 = DHE-RSA-AES256-GCM-SHA384 +ssl_options.ciphers.6 = DHE-DSS-AES256-GCM-SHA384 +ssl_options.ciphers.7 = ECDHE-ECDSA-AES128-GCM-SHA256 +ssl_options.ciphers.8 = ECDHE-RSA-AES128-GCM-SHA256 +ssl_options.ciphers.9 = ECDH-ECDSA-AES128-GCM-SHA256 +ssl_options.ciphers.10 = ECDH-RSA-AES128-GCM-SHA256 +ssl_options.ciphers.11 = DHE-RSA-AES128-GCM-SHA256 +ssl_options.ciphers.12 = DHE-DSS-AES128-GCM-SHA256 + +management.load_definitions = /opt/definitions.json + +# For connection events +event_exchange.vhost = devices \ No newline at end of file diff --git a/gitops/charts/onelab/templates/_helpers.tpl b/gitops/charts/onelab/templates/_helpers.tpl new file mode 100644 index 0000000..fdd6930 --- /dev/null +++ b/gitops/charts/onelab/templates/_helpers.tpl @@ -0,0 +1,25 @@ +{{- define "onelab.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "onelab.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 }} + +{{- define "onelab.labels" -}} +app.kubernetes.io/name: {{ include "onelab.name" . }} +helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | quote }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} + diff --git a/gitops/charts/onelab/templates/configmap-rabbit.yaml b/gitops/charts/onelab/templates/configmap-rabbit.yaml new file mode 100644 index 0000000..75c8b10 --- /dev/null +++ b/gitops/charts/onelab/templates/configmap-rabbit.yaml @@ -0,0 +1,18 @@ +{{- $cfg := fromYaml (.Files.AsConfig) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: onelab-rabbit-config + labels: + {{- include "onelab.labels" . | nindent 4 }} + annotations: + argocd.argoproj.io/sync-wave: {{ .Values.syncWaves.statefulDeps | quote }} +data: + rabbit.conf: | +{{ index $cfg "rabbit.conf" | nindent 4 }} + advanced.conf: | +{{ index $cfg "advanced.conf" | nindent 4 }} + enable_plugins: | +{{ index $cfg "enable_plugins" | nindent 4 }} + definitions.json: | +{{ index $cfg "rabbit-definitions.json" | nindent 4 }} diff --git a/gitops/charts/onelab/templates/configmap-revproxy.yaml b/gitops/charts/onelab/templates/configmap-revproxy.yaml new file mode 100644 index 0000000..08d7669 --- /dev/null +++ b/gitops/charts/onelab/templates/configmap-revproxy.yaml @@ -0,0 +1,14 @@ +{{- $cfg := fromYaml (.Files.AsConfig) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: onelab-revproxy + labels: + {{- include "onelab.labels" . | nindent 4 }} + annotations: + argocd.argoproj.io/sync-wave: {{ .Values.syncWaves.apps | quote }} +data: + nginx.conf: | +{{ tpl (index $cfg "nginx.conf.tpl") . | nindent 4 }} + error-404.html: | +{{ index $cfg "error-404.html" | nindent 4 }} diff --git a/gitops/charts/onelab/templates/deployment-optional-workers.yaml b/gitops/charts/onelab/templates/deployment-optional-workers.yaml new file mode 100644 index 0000000..13e2bf2 --- /dev/null +++ b/gitops/charts/onelab/templates/deployment-optional-workers.yaml @@ -0,0 +1,111 @@ +{{- $root := . }} +{{- if .Values.features.ldapWorker }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ldap-worker + labels: + app.kubernetes.io/component: ldap-worker + {{- include "onelab.labels" $root | nindent 4 }} + annotations: + argocd.argoproj.io/sync-wave: {{ $root.Values.syncWaves.apps | quote }} +spec: + replicas: {{ $root.Values.replicas.ldap | default 1 }} + selector: + matchLabels: + app.kubernetes.io/component: ldap-worker + app.kubernetes.io/name: {{ include "onelab.name" $root }} + app.kubernetes.io/instance: {{ $root.Release.Name }} + template: + metadata: + labels: + app.kubernetes.io/component: ldap-worker + app.kubernetes.io/name: {{ include "onelab.name" $root }} + app.kubernetes.io/instance: {{ $root.Release.Name }} + spec: + {{- with $root.Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: ldap-worker + image: {{ printf "%s/%s:%s" $root.Values.images.registry "onelab-ldap-worker" $root.Values.images.tag | quote }} + volumeMounts: + - name: configurations + mountPath: /conf/configurations.yml + subPath: configurations.yml + readOnly: true + {{- if eq $root.Values.persistence.mode "hostPath" }} + - name: logs + mountPath: /logs + {{- end }} + volumes: + - name: configurations + secret: + secretName: onelab-configurations + {{- if eq $root.Values.persistence.mode "hostPath" }} + - name: logs + hostPath: + path: {{ $root.Values.persistence.hostPath.logs }} + type: DirectoryOrCreate + {{- else }} + - name: logs + emptyDir: {} + {{- end }} +{{- end }} +{{- if .Values.features.mailerWorker }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mailer-worker + labels: + app.kubernetes.io/component: mailer-worker + {{- include "onelab.labels" $root | nindent 4 }} + annotations: + argocd.argoproj.io/sync-wave: {{ $root.Values.syncWaves.apps | quote }} +spec: + replicas: {{ $root.Values.replicas.mailer | default 1 }} + selector: + matchLabels: + app.kubernetes.io/component: mailer-worker + app.kubernetes.io/name: {{ include "onelab.name" $root }} + app.kubernetes.io/instance: {{ $root.Release.Name }} + template: + metadata: + labels: + app.kubernetes.io/component: mailer-worker + app.kubernetes.io/name: {{ include "onelab.name" $root }} + app.kubernetes.io/instance: {{ $root.Release.Name }} + spec: + {{- with $root.Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: mailer-worker + image: {{ printf "%s/%s:%s" $root.Values.images.registry "onelab-mailer-worker" $root.Values.images.tag | quote }} + volumeMounts: + - name: configurations + mountPath: /conf/configurations.yml + subPath: configurations.yml + readOnly: true + {{- if eq $root.Values.persistence.mode "hostPath" }} + - name: logs + mountPath: /logs + {{- end }} + volumes: + - name: configurations + secret: + secretName: onelab-configurations + {{- if eq $root.Values.persistence.mode "hostPath" }} + - name: logs + hostPath: + path: {{ $root.Values.persistence.hostPath.logs }} + type: DirectoryOrCreate + {{- else }} + - name: logs + emptyDir: {} + {{- end }} +{{- end }} diff --git a/gitops/charts/onelab/templates/deployment-redis.yaml b/gitops/charts/onelab/templates/deployment-redis.yaml new file mode 100644 index 0000000..de8bec4 --- /dev/null +++ b/gitops/charts/onelab/templates/deployment-redis.yaml @@ -0,0 +1,53 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: redis + labels: + {{- include "onelab.labels" . | nindent 4 }} + annotations: + argocd.argoproj.io/sync-wave: {{ .Values.syncWaves.statefulDeps | quote }} +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/component: redis + app.kubernetes.io/name: {{ include "onelab.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + template: + metadata: + labels: + app.kubernetes.io/component: redis + app.kubernetes.io/name: {{ include "onelab.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: redis + image: {{ printf "%s/%s:%s" .Values.images.registry .Values.images.redis .Values.images.redisTag | quote }} + ports: + - containerPort: 6379 + name: redis + {{- with .Values.redis.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} +--- +apiVersion: v1 +kind: Service +metadata: + name: redis + labels: + {{- include "onelab.labels" . | nindent 4 }} +spec: + type: ClusterIP + ports: + - port: 6379 + targetPort: redis + name: redis + selector: + app.kubernetes.io/component: redis + app.kubernetes.io/name: {{ include "onelab.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} diff --git a/gitops/charts/onelab/templates/deployment-revproxy.yaml b/gitops/charts/onelab/templates/deployment-revproxy.yaml new file mode 100644 index 0000000..0c43163 --- /dev/null +++ b/gitops/charts/onelab/templates/deployment-revproxy.yaml @@ -0,0 +1,73 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: revproxy + labels: + {{- include "onelab.labels" . | nindent 4 }} + annotations: + argocd.argoproj.io/sync-wave: {{ .Values.syncWaves.apps | quote }} +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/component: revproxy + app.kubernetes.io/name: {{ include "onelab.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + template: + metadata: + labels: + app.kubernetes.io/component: revproxy + app.kubernetes.io/name: {{ include "onelab.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: nginx + image: {{ printf "%s/%s:%s" .Values.images.registry .Values.images.nginx .Values.images.nginxTag | quote }} + ports: + - containerPort: 80 + name: http + volumeMounts: + - name: nginx + mountPath: /etc/nginx/nginx.conf + subPath: nginx.conf + - name: nginx + mountPath: /data/error-404.html + subPath: error-404.html + - name: data + mountPath: /data + volumes: + - name: nginx + configMap: + name: onelab-revproxy + - name: data + {{- if eq .Values.persistence.mode "hostPath" }} + hostPath: + path: {{ .Values.persistence.hostPath.data }} + type: DirectoryOrCreate + {{- else }} + emptyDir: {} + {{- end }} +--- +apiVersion: v1 +kind: Service +metadata: + name: revproxy + labels: + {{- include "onelab.labels" . | nindent 4 }} +spec: + type: {{ .Values.revproxy.serviceType }} + ports: + - port: 80 + targetPort: http + name: http + {{- if and (eq .Values.revproxy.serviceType "NodePort") .Values.revproxy.nodePort }} + nodePort: {{ .Values.revproxy.nodePort }} + {{- end }} + selector: + app.kubernetes.io/component: revproxy + app.kubernetes.io/name: {{ include "onelab.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} diff --git a/gitops/charts/onelab/templates/secret-configurations.yaml b/gitops/charts/onelab/templates/secret-configurations.yaml new file mode 100644 index 0000000..6523394 --- /dev/null +++ b/gitops/charts/onelab/templates/secret-configurations.yaml @@ -0,0 +1,13 @@ +{{- $cfg := fromYaml (.Files.AsConfig) }} +apiVersion: v1 +kind: Secret +metadata: + name: onelab-configurations + labels: + {{- include "onelab.labels" . | nindent 4 }} + annotations: + argocd.argoproj.io/sync-wave: {{ .Values.syncWaves.statefulDeps | quote }} +type: Opaque +stringData: + configurations.yml: | +{{- tpl (index $cfg "configurations.gotmpl") . | nindent 4 }} diff --git a/gitops/charts/onelab/templates/secret-postgres.yaml b/gitops/charts/onelab/templates/secret-postgres.yaml new file mode 100644 index 0000000..ed6a0d1 --- /dev/null +++ b/gitops/charts/onelab/templates/secret-postgres.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: onelab-postgres + labels: + {{- include "onelab.labels" . | nindent 4 }} + annotations: + argocd.argoproj.io/sync-wave: {{ .Values.syncWaves.postgres | quote }} +type: Opaque +stringData: + postgres-password: {{ .Values.postgresql.auth.password | quote }} diff --git a/gitops/charts/onelab/templates/secret-rabbit-tls.yaml b/gitops/charts/onelab/templates/secret-rabbit-tls.yaml new file mode 100644 index 0000000..65e5fdd --- /dev/null +++ b/gitops/charts/onelab/templates/secret-rabbit-tls.yaml @@ -0,0 +1,18 @@ +{{- if .Values.rabbitmq.tls.embed }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Values.rabbitmq.tls.secretName }} + labels: + {{- include "onelab.labels" . | nindent 4 }} + annotations: + argocd.argoproj.io/sync-wave: {{ .Values.syncWaves.statefulDeps | quote }} +type: Opaque +stringData: + rabbit.crt: | +{{ .Values.rabbitmq.tls.crt | nindent 4 }} + rabbit.key: | +{{ .Values.rabbitmq.tls.key | nindent 4 }} + rabbit.fullchain.pem: | +{{ .Values.rabbitmq.tls.fullchain | nindent 4 }} +{{- end }} diff --git a/gitops/charts/onelab/templates/service-db.yaml b/gitops/charts/onelab/templates/service-db.yaml new file mode 100644 index 0000000..4e27fd3 --- /dev/null +++ b/gitops/charts/onelab/templates/service-db.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + name: db + labels: + {{- include "onelab.labels" . | nindent 4 }} + annotations: + argocd.argoproj.io/sync-wave: {{ .Values.syncWaves.postgres | quote }} +spec: + type: ClusterIP + ports: + - port: 5432 + targetPort: postgres + name: postgres + selector: + app.kubernetes.io/component: postgres + app.kubernetes.io/name: {{ include "onelab.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} diff --git a/gitops/charts/onelab/templates/service-rabbitmq.yaml b/gitops/charts/onelab/templates/service-rabbitmq.yaml new file mode 100644 index 0000000..e19fa7d --- /dev/null +++ b/gitops/charts/onelab/templates/service-rabbitmq.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: rabbitmq + labels: + {{- include "onelab.labels" . | nindent 4 }} +spec: + type: ClusterIP + ports: + - port: 5671 + targetPort: amqps + name: amqps + selector: + app.kubernetes.io/component: rabbitmq + app.kubernetes.io/name: {{ include "onelab.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} diff --git a/gitops/charts/onelab/templates/statefulset-postgres.yaml b/gitops/charts/onelab/templates/statefulset-postgres.yaml new file mode 100644 index 0000000..e0f0bcf --- /dev/null +++ b/gitops/charts/onelab/templates/statefulset-postgres.yaml @@ -0,0 +1,61 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: db + labels: + {{- include "onelab.labels" . | nindent 4 }} + annotations: + argocd.argoproj.io/sync-wave: {{ .Values.syncWaves.postgres | quote }} +spec: + replicas: 1 + serviceName: db + selector: + matchLabels: + app.kubernetes.io/component: postgres + app.kubernetes.io/name: {{ include "onelab.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + template: + metadata: + labels: + app.kubernetes.io/component: postgres + app.kubernetes.io/name: {{ include "onelab.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: postgres + image: {{ printf "%s/%s:%s" .Values.images.registry .Values.images.postgres .Values.images.postgresTag | quote }} + ports: + - containerPort: 5432 + name: postgres + env: + - name: POSTGRES_USER + value: postgres + - name: POSTGRES_DB + value: postgres + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: onelab-postgres + key: postgres-password + volumeMounts: + - name: data + mountPath: /var/lib/postgresql/data + {{- with .Values.postgresql.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeClaimTemplates: + - metadata: + name: data + spec: + accessModes: ["ReadWriteOnce"] + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.postgres.size | quote }} diff --git a/gitops/charts/onelab/templates/statefulset-rabbitmq.yaml b/gitops/charts/onelab/templates/statefulset-rabbitmq.yaml new file mode 100644 index 0000000..16f35a7 --- /dev/null +++ b/gitops/charts/onelab/templates/statefulset-rabbitmq.yaml @@ -0,0 +1,80 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: rabbitmq + labels: + {{- include "onelab.labels" . | nindent 4 }} + annotations: + argocd.argoproj.io/sync-wave: {{ .Values.syncWaves.statefulDeps | quote }} +spec: + replicas: 1 + serviceName: rabbitmq + selector: + matchLabels: + app.kubernetes.io/component: rabbitmq + app.kubernetes.io/name: {{ include "onelab.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + template: + metadata: + labels: + app.kubernetes.io/component: rabbitmq + app.kubernetes.io/name: {{ include "onelab.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + spec: + hostname: onelab + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: rabbitmq + image: {{ printf "%s/%s:%s" .Values.images.registry .Values.images.rabbitmq .Values.images.rabbitmqTag | quote }} + ports: + - containerPort: 5671 + name: amqps + volumeMounts: + - name: rabbit-data + mountPath: /var/lib/rabbitmq/mnesia + - name: rabbit-config + mountPath: /etc/rabbitmq/rabbitmq.conf + subPath: rabbit.conf + - name: rabbit-config + mountPath: /etc/rabbitmq/advanced.conf + subPath: advanced.conf + - name: rabbit-config + mountPath: /etc/rabbitmq/enabled_plugins + subPath: enable_plugins + - name: rabbit-config + mountPath: /opt/definitions.json + subPath: definitions.json + - name: rabbit-tls + mountPath: /etc/rabbitmq/ssl/rabbit.crt + subPath: rabbit.crt + - name: rabbit-tls + mountPath: /etc/rabbitmq/ssl/rabbit.key + subPath: rabbit.key + - name: rabbit-tls + mountPath: /etc/rabbitmq/ssl/rabbit.fullchain.pem + subPath: rabbit.fullchain.pem + {{- with .Values.rabbitmq.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumes: + - name: rabbit-config + configMap: + name: onelab-rabbit-config + - name: rabbit-tls + secret: + secretName: {{ .Values.rabbitmq.tls.secretName }} + volumeClaimTemplates: + - metadata: + name: rabbit-data + spec: + accessModes: ["ReadWriteOnce"] + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.rabbitmq.size | quote }} diff --git a/gitops/charts/onelab/templates/workloads.yaml b/gitops/charts/onelab/templates/workloads.yaml new file mode 100644 index 0000000..3e20a01 --- /dev/null +++ b/gitops/charts/onelab/templates/workloads.yaml @@ -0,0 +1,116 @@ +{{- $root := . }} +{{- range .Values.workloads }} +{{- $n := .replicas | default 1 | int }} +{{- if and .replicaKey (hasKey $root.Values.replicas .replicaKey) }} +{{- $n = index $root.Values.replicas .replicaKey | int }} +{{- end }} +{{- $vols := or .config (not (empty .mounts)) }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .name }} + labels: + app.kubernetes.io/component: {{ .name }} + {{- include "onelab.labels" $root | nindent 4 }} + annotations: + argocd.argoproj.io/sync-wave: {{ $root.Values.syncWaves.apps | quote }} +spec: + replicas: {{ $n }} + selector: + matchLabels: + app.kubernetes.io/component: {{ .name }} + app.kubernetes.io/name: {{ include "onelab.name" $root }} + app.kubernetes.io/instance: {{ $root.Release.Name }} + template: + metadata: + labels: + app.kubernetes.io/component: {{ .name }} + app.kubernetes.io/name: {{ include "onelab.name" $root }} + app.kubernetes.io/instance: {{ $root.Release.Name }} + spec: + {{- with $root.Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: {{ .name }} + image: {{ printf "%s/%s:%s" $root.Values.images.registry .image $root.Values.images.tag | quote }} + {{- if .port }} + ports: + - containerPort: {{ .port }} + name: http + {{- end }} + {{- if and .website (not $root.Values.website.ssr) }} + env: + - name: RENDERING_MODE + value: "no-ssr" + {{- end }} + {{- if $vols }} + volumeMounts: + {{- if .config }} + - name: configurations + mountPath: /conf/configurations.yml + subPath: configurations.yml + readOnly: true + {{- end }} + {{- if has "logs" .mounts }} + - name: logs + mountPath: /logs + {{- end }} + {{- if has "data" .mounts }} + - name: data + mountPath: /data + {{- end }} + {{- if has "shared" .mounts }} + - name: data + mountPath: /shared-inputs + subPath: shared/inputs + - name: data + mountPath: /shared-archived + subPath: shared/archived + {{- end }} + {{- end }} + {{- if $vols }} + volumes: + {{- if .config }} + - name: configurations + secret: + secretName: onelab-configurations + {{- end }} + {{- if eq $root.Values.persistence.mode "hostPath" }} + {{- if has "logs" .mounts }} + - name: logs + hostPath: + path: {{ $root.Values.persistence.hostPath.logs }} + type: DirectoryOrCreate + {{- end }} + {{- if or (has "data" .mounts) (has "shared" .mounts) }} + - name: data + hostPath: + path: {{ $root.Values.persistence.hostPath.data }} + type: DirectoryOrCreate + {{- end }} + {{- end }} + {{- end }} +{{- if and .port (gt (int .port) 0) }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .name }} + labels: + app.kubernetes.io/component: {{ .name }} + {{- include "onelab.labels" $root | nindent 4 }} +spec: + type: ClusterIP + ports: + - port: {{ .port }} + targetPort: http + name: http + selector: + app.kubernetes.io/component: {{ .name }} + app.kubernetes.io/name: {{ include "onelab.name" $root }} + app.kubernetes.io/instance: {{ $root.Release.Name }} +{{- end }} +{{- end }} diff --git a/gitops/charts/onelab/values.yaml b/gitops/charts/onelab/values.yaml new file mode 100644 index 0000000..c253f7c --- /dev/null +++ b/gitops/charts/onelab/values.yaml @@ -0,0 +1,187 @@ +# Default values for onelab — override per environment (see gitops/values/). + +nameOverride: "" +fullnameOverride: "" + +images: + registry: hub.andrewalliance.com/releases + tag: "1.27.0" + nginx: nginx + nginxTag: "1.29.5-alpine" + postgres: postgres + postgresTag: "17.8" + redis: redis + redisTag: "7.4.7-alpine" + rabbitmq: rabbitmq + rabbitmqTag: "3.13.7" + +imagePullSecrets: [] +# - name: hub-andrewalliance + +# hostPath: matches typical single-node Swarm-style install (shared /data and /logs). +# Use persistence.mode: pvc + a ReadWriteMany class for multi-node shared storage. +persistence: + mode: hostPath + storageClass: "" + hostPath: + data: /opt/onelab/data + logs: /opt/onelab/logs + postgres: + size: 20Gi + rabbitmq: + size: 5Gi + +postgresql: + auth: + password: "changeme-use-strong-password" + resources: {} + +redis: + resources: {} + +rabbitmq: + resources: {} + # TLS: create Secret `onelab-rabbit-tls` in the release namespace (see README), or set embed: true. + tls: + secretName: onelab-rabbit-tls + embed: false + crt: "" + key: "" + fullchain: "" + +syncWaves: + postgres: "-3" + statefulDeps: "-2" + apps: "0" + +onelab: + domain: "https://localhost" + mailer: + noreply: "no-reply@andrewalliance.com" + secrets: + authTokenKey: "replace-auth-token-key" + monitoringToken: "replace-monitoring-token" + rabbitToken: "replace-rabbit-token" + intercom: + appid: "" + secret: "replace-intercom-secret" + +features: + ldapWorker: false + mailerWorker: false + +website: + ssr: true + +revproxy: + serviceType: NodePort + nodePort: 30080 + ipv6Listen: true + +# Replica counts (api.apidevice etc. override defaults in templates/workloads.yaml via this map) +replicas: + api: 2 + apidevice: 1 + apirabbit: 1 + devices: 1 + experiments: 1 + images: 1 + manual: 1 + ws: 1 + ldap: 1 + mailer: 1 + +resources: {} + +workloads: + - name: supervisor + image: onelab-supervisor-worker + replicas: 1 + port: 0 + config: true + mounts: [logs, data] + - name: file-worker + image: onelab-file-worker + replicas: 1 + port: 0 + config: true + mounts: [logs, data, shared] + - name: api + image: onelab-api + replicaKey: api + port: 3000 + config: true + mounts: [logs, data] + - name: api-device + image: onelab-api-device + replicaKey: apidevice + port: 3000 + config: true + mounts: [logs, data] + - name: api-rabbit + image: onelab-api-rabbit + replicaKey: apirabbit + port: 3000 + config: true + mounts: [logs, data] + - name: devices-worker + image: onelab-devices-worker + replicaKey: devices + port: 0 + config: true + mounts: [logs, data] + - name: experiments-worker + image: onelab-experiments-worker + replicaKey: experiments + port: 0 + config: true + mounts: [logs] + - name: images-worker + image: onelab-images-worker + replicaKey: images + port: 0 + config: true + mounts: [logs, data] + - name: manual-worker + image: onelab-manual-worker + replicaKey: manual + port: 0 + config: true + mounts: [logs] + - name: websocket-worker + image: onelab-websocket-worker + replicaKey: ws + port: 3030 + config: true + mounts: [logs] + - name: static + image: onelab-static + replicas: 1 + port: 80 + config: false + mounts: [] + - name: main + image: onelab-main + replicas: 1 + port: 80 + config: false + mounts: [] + - name: designer + image: onelab-designer + replicas: 1 + port: 80 + config: false + mounts: [] + - name: runner + image: onelab-runner + replicas: 1 + port: 80 + config: false + mounts: [] + - name: website + image: onelab-website + replicas: 1 + port: 4000 + config: false + mounts: [] + website: true diff --git a/gitops/values/k3s-example.yaml b/gitops/values/k3s-example.yaml new file mode 100644 index 0000000..7724bcc --- /dev/null +++ b/gitops/values/k3s-example.yaml @@ -0,0 +1,31 @@ +# k3s / Argo CD overlay (private Git — rotate secrets if this file is ever made public). +# Add image pull credentials when using hub.andrewalliance.com: +# kubectl create secret docker-registry hub-andrewalliance -n onelab \ +# --docker-server=hub.andrewalliance.com --docker-username=... --docker-password=... +# then set imagePullSecrets below. + +imagePullSecrets: [] + +persistence: + mode: hostPath + hostPath: + data: /opt/onelab/data + logs: /opt/onelab/logs + +postgresql: + auth: + password: "9daLpcV7vKS1zXUElQRO5h4u" + +onelab: + domain: "https://onelab.example.com" + secrets: + authTokenKey: "ntH0Yd3AcsqwMu7ah8xLbWFS4BK5GUmi" + monitoringToken: "Cj4ix7wdg8XPIsDAFENKRTmh6lkvBLZp" + rabbitToken: "GmSWRv14PXZuyM5QDgb8wpxk0dh7F6IJ" + intercom: + appid: "" + secret: "" + +revproxy: + serviceType: NodePort + nodePort: 30080 diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..6b8b15a --- /dev/null +++ b/install.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# Define OneLab along with the Ansible logs directory + +directory="../onelab/logs/ansible" + +# Check if the directory structure exists +if [ ! -d "$directory" ]; then + # If it doesn't exist, create it + mkdir -p "$directory" +fi + +# To allow writing the logs +chown -R $USER "$directory" + +ANSIBLE_LOG_PATH="$directory/$(date +%F)-ansible.log" ansible-playbook ./app/playbooks/install.yml \ No newline at end of file diff --git a/resources/restore/restore.sh b/resources/restore/restore.sh new file mode 100644 index 0000000..92590ba --- /dev/null +++ b/resources/restore/restore.sh @@ -0,0 +1,3 @@ + +#!/bin/bash +ANSIBLE_LOG_PATH=../../logs/ansible/$(date +%F)-ansible.log ansible-playbook ./restore.yml diff --git a/resources/restore/restore.yml b/resources/restore/restore.yml new file mode 100644 index 0000000..6feb685 --- /dev/null +++ b/resources/restore/restore.yml @@ -0,0 +1,104 @@ +--- +- name: Restoring OneLab (1.27.0) + hosts: localhost + vars_prompt: + - name: "userchoice" + prompt: "Confirm OneLab 1.27.0 restore: yes/no" + private: no + vars: + onelab_path: '../..' + onelab_images_path: '../../../onelab-images-1.27.0.tar.gz' + main_path: '../../..' + + tasks: + - name: Checking if the OneLab images bundle is existing + stat: + path: "{{ onelab_images_path }}" + get_checksum: no + register: docker_images_tar + + - name: Aborting the restore process + fail: + msg: Aborting the restore process + when: userchoice != "yes" + + # Stop OneLab if OneLab is running + - name: Checking if OneLab is running + shell: docker stack ls | grep 'onelab' | wc -l + become: true + register: stack_count + + # Stop OneLab (unkown version) if OneLab is running + - include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/stop-onelab.yml" + when: stack_count.stdout != "0" + - include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/stop-proxy.yml" + when: stack_count.stdout != "0" + + # Load docker images + - name: Loading Docker images + debug: + msg: + - "Loading images could take few minutes, please do not interrupt" + when: (docker_images_tar is defined) and (docker_images_tar.stat.exists) + + - name: Loading OneLab images from the tar file + shell: + docker load --input {{ onelab_images_path }} + become: true + when: (docker_images_tar is defined) and (docker_images_tar.stat.exists) + + - name: Unzipping backup + shell: | + tar xzvf ./onelab.tar.gz -C ./../.. + become: true + + - name: Cleaning latest installation + ansible.builtin.file: + path: "{{ onelab_path }}/installation/latest" + state: absent + + - name: Flagging restored version as current + ansible.builtin.copy: + src: ../../installation/1.27.0/ + dest: ../../installation/latest + directory_mode: no + remote_src: yes + + # Load configuration + - include_vars: + file: "{{ onelab_path }}/configurations.yml" + + # Perform reconfiguration + - include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/reconfigure-task.yml" + + # Checking if volumes are existing + - name: Getting the list of docker volumes + shell: docker volume ls + become: true + register: volumes_result + + # Removing volumes + + - name: Resetting postgres database & cleaning cache + shell: | + docker volume rm onelab_pgdata -f + docker volume rm onelab_rabbitmq_data -f + become: true + when: "'rabbitmq' in volumes_result.stdout or 'pgdata' in volumes_result.stdout" + + # Restoring database + + - include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/start-db-only.yml" + + - name: Restoring database + shell: > + docker exec -i $(docker ps --filter "name=onelab_db|onelab-db" -q) pg_restore -Upostgres -dpostgres -v -Fc < ./db.tar.gz + become: true + + - include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/stop-db-only.yml" + + # Start OneLab if OneLab was running + - include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/start-proxy.yml" + when: stack_count.stdout != "0" + - include_tasks: "{{ onelab_path }}/installation/latest/app/playbooks/tasks/start-onelab.yml" + when: stack_count.stdout != "0" diff --git a/resources/scripts/centos/install_ansible.sh b/resources/scripts/centos/install_ansible.sh new file mode 100644 index 0000000..3da1831 --- /dev/null +++ b/resources/scripts/centos/install_ansible.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +sudo yum install dnf + +# update the DNF package repository cache +sudo dnf makecache + +# To enable EPEL repository, install the epel-release package +sudo dnf install epel-release -y + +# update the DNF package repository cache again +sudo dnf makecache + +# Install Ansible +sudo dnf install ansible -y + +# Create the ansible log file +sudo touch /var/log/ansible.log + +# Provide permissions for ansible log file +sudo chmod 666 /var/log/ansible.log + +# Update Ansible hosts file for local connection +sudo sed -i '$ a localhost ansible_connection=local' /etc/ansible/hosts + +# Update Ansible configuration file with logs path +sudo sed -i '13 a log_path = /var/log/ansible.log' /etc/ansible/ansible.cfg diff --git a/resources/scripts/centos/install_docker.sh b/resources/scripts/centos/install_docker.sh new file mode 100644 index 0000000..0c08dda --- /dev/null +++ b/resources/scripts/centos/install_docker.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# Install the yum-utils package (which provides the yum-config-manager utility) and set up the stable repository + +sudo yum install -y yum-utils +sudo yum-config-manager \ + --add-repo \ + https://download.docker.com/linux/centos/docker-ce.repo + +# Install Docker Engine +sudo yum install docker-ce docker-ce-cli containerd.io -y + +# Start Docker +sudo systemctl start docker + +# Enable Docker +sudo systemctl enable docker diff --git a/resources/scripts/debian10/install_ansible.sh b/resources/scripts/debian10/install_ansible.sh new file mode 100644 index 0000000..26e2448 --- /dev/null +++ b/resources/scripts/debian10/install_ansible.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +sudo sed -i '$ a deb http://ppa.launchpad.net/ansible/ansible/ubuntu trusty main' /etc/apt/sources.list +sudo apt-get update +sudo apt-get install -y gnupg2 + +sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 93C4A3FD7BB9C367 +sudo apt update +sudo apt install -y ansible + +# Create the ansible log file +sudo touch /var/log/ansible.log + +# Provide permissions for ansible log file +sudo chmod 666 /var/log/ansible.log + +# Update Ansible hosts file for local connection +sudo sed -i '$ a localhost ansible_connection=local' /etc/ansible/hosts + +# Update Ansible configuration file with logs path +sudo sed -i '13 a log_path = /var/log/ansible.log' /etc/ansible/ansible.cfg + diff --git a/resources/scripts/debian10/install_docker.sh b/resources/scripts/debian10/install_docker.sh new file mode 100644 index 0000000..3c2a404 --- /dev/null +++ b/resources/scripts/debian10/install_docker.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Install Dependencies +sudo apt-get update +sudo apt-get install \ + apt-transport-https \ + ca-certificates \ + curl \ + gnupg \ + lsb-release -y + +# Install Docker +curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor --yes -o /usr/share/keyrings/docker-archive-keyring.gpg + +echo \ + "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \ + $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null + +sudo apt-get update +sudo apt-get install docker-ce docker-ce-cli containerd.io -y + +sudo systemctl start docker +sudo systemctl enable docker + +# Add user to docker group +sudo usermod -aG docker $USER diff --git a/resources/scripts/debian11/install_ansible.sh b/resources/scripts/debian11/install_ansible.sh new file mode 100644 index 0000000..0373293 --- /dev/null +++ b/resources/scripts/debian11/install_ansible.sh @@ -0,0 +1,13 @@ +#!/bin/bash +sudo apt update + +sudo apt install ansible + +# Create the ansible log file +sudo touch /var/log/ansible.log + +# Provide permissions for ansible log file +sudo chmod 666 /var/log/ansible.log + + + diff --git a/resources/scripts/debian11/install_docker.sh b/resources/scripts/debian11/install_docker.sh new file mode 100644 index 0000000..bc47a9a --- /dev/null +++ b/resources/scripts/debian11/install_docker.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# Add Docker's official GPG key: +sudo apt-get update +sudo apt-get install ca-certificates curl +sudo install -m 0755 -d /etc/apt/keyrings +sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc +sudo chmod a+r /etc/apt/keyrings/docker.asc + +# Add the repository to Apt sources: +echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \ + $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ + sudo tee /etc/apt/sources.list.d/docker.list > /dev/null +sudo apt-get update +sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin +sudo usermod -aG docker $USER diff --git a/resources/scripts/debian12/install_ansible.sh b/resources/scripts/debian12/install_ansible.sh new file mode 100644 index 0000000..0373293 --- /dev/null +++ b/resources/scripts/debian12/install_ansible.sh @@ -0,0 +1,13 @@ +#!/bin/bash +sudo apt update + +sudo apt install ansible + +# Create the ansible log file +sudo touch /var/log/ansible.log + +# Provide permissions for ansible log file +sudo chmod 666 /var/log/ansible.log + + + diff --git a/resources/scripts/debian12/install_docker.sh b/resources/scripts/debian12/install_docker.sh new file mode 100644 index 0000000..bc47a9a --- /dev/null +++ b/resources/scripts/debian12/install_docker.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# Add Docker's official GPG key: +sudo apt-get update +sudo apt-get install ca-certificates curl +sudo install -m 0755 -d /etc/apt/keyrings +sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc +sudo chmod a+r /etc/apt/keyrings/docker.asc + +# Add the repository to Apt sources: +echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \ + $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ + sudo tee /etc/apt/sources.list.d/docker.list > /dev/null +sudo apt-get update +sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin +sudo usermod -aG docker $USER diff --git a/resources/scripts/fedora/install_ansible.sh b/resources/scripts/fedora/install_ansible.sh new file mode 100644 index 0000000..77dd084 --- /dev/null +++ b/resources/scripts/fedora/install_ansible.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +sudo dnf install ansible -y + +#Install openssl +sudo yum install openssl -y + +# Create the ansible log file +sudo touch /var/log/ansible.log + +# Provide permissions for ansible log file +sudo chmod 666 /var/log/ansible.log + +# Update Ansible hosts file for local connection +sudo sed -i '$ a localhost ansible_connection=local' /etc/ansible/hosts + +# Update Ansible configuration file with logs path +sudo sed -i '13 a log_path = /var/log/ansible.log' /etc/ansible/ansible.cfg + diff --git a/resources/scripts/fedora/install_docker.sh b/resources/scripts/fedora/install_docker.sh new file mode 100644 index 0000000..256553c --- /dev/null +++ b/resources/scripts/fedora/install_docker.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Install the dnf-plugins-core package (which provides the commands to manage your DNF repositories) + +sudo dnf -y install dnf-plugins-core -y + +# Setup stable repository + +sudo dnf config-manager \ + --add-repo \ + https://download.docker.com/linux/fedora/docker-ce.repo -y + +# Install the latest version of Docker Engine and containerd + +sudo dnf install docker-ce docker-ce-cli containerd.io -y + +sudo systemctl start docker + +sudo systemctl enable docker + +sudo usermod -aG docker $USER \ No newline at end of file diff --git a/resources/scripts/redhat7/install_ansible.sh b/resources/scripts/redhat7/install_ansible.sh new file mode 100644 index 0000000..8ba9664 --- /dev/null +++ b/resources/scripts/redhat7/install_ansible.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# Install Extra Packages for Linux +sudo yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm -y + +# Install Ansible +sudo yum install ansible -y + +# Create the ansible log file +sudo touch /var/log/ansible.log + +# Provide permissions for ansible log file +sudo chmod 666 /var/log/ansible.log + +# Update Ansible hosts file for local connection +sudo sed -i '$ a localhost ansible_connection=local' /etc/ansible/hosts + +# Update Ansible configuration file with logs path +sudo sed -i '13 a log_path = /var/log/ansible.log' /etc/ansible/ansible.cfg + diff --git a/resources/scripts/redhat7/install_docker.sh b/resources/scripts/redhat7/install_docker.sh new file mode 100644 index 0000000..1e0cb94 --- /dev/null +++ b/resources/scripts/redhat7/install_docker.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Install the yum-utils package + +sudo yum install -y yum-utils + +sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo + +# RHEL-7 requires the below dependencies to be installed to setup Docker + +sudo yum install http://mirror.centos.org/centos/7/extras/x86_64/Packages/container-selinux-2.119.2-1.911c772.el7_8.noarch.rpm -y + +sudo yum install http://mirror.centos.org/centos/7/extras/x86_64/Packages/fuse3-libs-3.6.1-4.el7.x86_64.rpm -y + +sudo yum install http://mirror.centos.org/centos/7/extras/x86_64/Packages/fuse-overlayfs-0.7.2-6.el7_8.x86_64.rpm -y + +sudo yum install http://mirror.centos.org/centos/7/extras/x86_64/Packages/slirp4netns-0.4.3-4.el7_8.x86_64.rpm -y + +# Ended dependencies installation + +sudo yum install docker-ce -y + +sudo systemctl start docker + +sudo systemctl enable docker + +sudo usermod -aG docker $USER \ No newline at end of file diff --git a/resources/scripts/redhat8/install_ansible.sh b/resources/scripts/redhat8/install_ansible.sh new file mode 100644 index 0000000..35f2168 --- /dev/null +++ b/resources/scripts/redhat8/install_ansible.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +sudo dnf update + +sudo dnf install ansible-core \ No newline at end of file diff --git a/resources/scripts/redhat8/install_docker.sh b/resources/scripts/redhat8/install_docker.sh new file mode 100644 index 0000000..1bc2877 --- /dev/null +++ b/resources/scripts/redhat8/install_docker.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +sudo dnf update -y + +sudo dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo + +# To verify which version of docker is available for installation +# sudo dnf list docker-ce + +sudo dnf install docker-ce --nobest -y +sudo systemctl start docker +sudo systemctl enable docker + +# Get current user and add to 'docker' group +sudo usermod -aG docker $USER diff --git a/resources/scripts/redhat9/install_ansible.sh b/resources/scripts/redhat9/install_ansible.sh new file mode 100644 index 0000000..35f2168 --- /dev/null +++ b/resources/scripts/redhat9/install_ansible.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +sudo dnf update + +sudo dnf install ansible-core \ No newline at end of file diff --git a/resources/scripts/redhat9/install_docker.sh b/resources/scripts/redhat9/install_docker.sh new file mode 100644 index 0000000..1bc2877 --- /dev/null +++ b/resources/scripts/redhat9/install_docker.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +sudo dnf update -y + +sudo dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo + +# To verify which version of docker is available for installation +# sudo dnf list docker-ce + +sudo dnf install docker-ce --nobest -y +sudo systemctl start docker +sudo systemctl enable docker + +# Get current user and add to 'docker' group +sudo usermod -aG docker $USER diff --git a/resources/scripts/ubuntu/install_ansible.sh b/resources/scripts/ubuntu/install_ansible.sh new file mode 100644 index 0000000..b231165 --- /dev/null +++ b/resources/scripts/ubuntu/install_ansible.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Install Dependencies +sudo apt update +sudo apt install software-properties-common + +# Install Ansible +sudo add-apt-repository --yes --update ppa:ansible/ansible +sudo apt update +sudo apt install ansible + +# Create the ansible log file +sudo touch /var/log/ansible.log + +# Provide permissions for ansible log file +sudo chmod 666 /var/log/ansible.log + +# Update Ansible hosts file for local connection +sudo sed -i '$ a localhost ansible_connection=local' /etc/ansible/hosts + +# Update Ansible configuration file with logs path +sudo sed -i '13 a log_path = /var/log/ansible.log' /etc/ansible/ansible.cfg \ No newline at end of file diff --git a/resources/scripts/ubuntu/install_docker.sh b/resources/scripts/ubuntu/install_docker.sh new file mode 100644 index 0000000..091bea7 --- /dev/null +++ b/resources/scripts/ubuntu/install_docker.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# Install Dependencies +sudo apt-get update +sudo apt-get install \ + apt-transport-https \ + ca-certificates \ + curl \ + gnupg \ + lsb-release + +# Install Docker +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor --yes -o /usr/share/keyrings/docker-archive-keyring.gpg + +echo \ + "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ + $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null + +sudo apt-get update +sudo apt-get install docker-ce docker-ce-cli containerd.io + +# Add user to docker group +sudo usermod -aG docker $USER diff --git a/resources/services/onelab.service b/resources/services/onelab.service new file mode 100644 index 0000000..a963ffd --- /dev/null +++ b/resources/services/onelab.service @@ -0,0 +1,14 @@ +# /etc/systemd/system/onelab.service +[Unit] +Description=OneLab +Requires=docker.service +After=docker.service + +[Service] +Type=simple +RemainAfterExit=yes +ExecStart={{ ansiblebin_result.stdout }} {{ playbook_dir.split('/onelab-enterprise-installer')[0].split('/onelab/installation')[0] }}/onelab/installation/latest/app/playbooks/deploy.yml +ExecStop={{ ansiblebin_result.stdout }} {{ playbook_dir.split('/onelab-enterprise-installer')[0].split('/onelab/installation')[0] }}/onelab/installation/latest/app/playbooks/stop.yml + +[Install] +WantedBy=default.target \ No newline at end of file