From 77ac0ce7a64825ff89e3388fc2ab91ab3b12ac2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Thu, 18 Apr 2019 20:14:17 +0200 Subject: [PATCH 01/24] Rename db-migration-worker to maintenance-worker --- backend/.dockerignore | 2 +- backend/db-migration-worker/.ssh/known_hosts | 3 --- ...se.db-migration.yml => docker-compose.maintenance.yml} | 8 ++++---- .../.dockerignore | 0 .../.gitignore | 0 .../Dockerfile | 0 .../{db-migration-worker => maintenance-worker}/README.md | 2 +- .../migrate.sh | 0 .../migration/mongo/import.sh | 0 .../migration/neo4j/badges.cql | 0 .../migration/neo4j/categories.cql | 0 .../migration/neo4j/comments.cql | 0 .../migration/neo4j/contributions.cql | 0 .../migration/neo4j/follows.cql | 0 .../migration/neo4j/import.sh | 0 .../migration/neo4j/shouts.cql | 0 .../migration/neo4j/users.cql | 0 .../sync_uploads.sh | 0 deployment/db-migration-worker.yaml | 8 ++++---- deployment/legacy-migration/deployment-backend.yaml | 6 +++--- deployment/legacy-migration/deployment-neo4j.yaml | 6 +++--- 21 files changed, 16 insertions(+), 19 deletions(-) delete mode 100644 backend/db-migration-worker/.ssh/known_hosts rename backend/{docker-compose.db-migration.yml => docker-compose.maintenance.yml} (82%) rename backend/{db-migration-worker => maintenance-worker}/.dockerignore (100%) rename backend/{db-migration-worker => maintenance-worker}/.gitignore (100%) rename backend/{db-migration-worker => maintenance-worker}/Dockerfile (100%) rename backend/{db-migration-worker => maintenance-worker}/README.md (96%) rename backend/{db-migration-worker => maintenance-worker}/migrate.sh (100%) rename backend/{db-migration-worker => maintenance-worker}/migration/mongo/import.sh (100%) rename backend/{db-migration-worker => maintenance-worker}/migration/neo4j/badges.cql (100%) rename backend/{db-migration-worker => maintenance-worker}/migration/neo4j/categories.cql (100%) rename backend/{db-migration-worker => maintenance-worker}/migration/neo4j/comments.cql (100%) rename backend/{db-migration-worker => maintenance-worker}/migration/neo4j/contributions.cql (100%) rename backend/{db-migration-worker => maintenance-worker}/migration/neo4j/follows.cql (100%) rename backend/{db-migration-worker => maintenance-worker}/migration/neo4j/import.sh (100%) rename backend/{db-migration-worker => maintenance-worker}/migration/neo4j/shouts.cql (100%) rename backend/{db-migration-worker => maintenance-worker}/migration/neo4j/users.cql (100%) rename backend/{db-migration-worker => maintenance-worker}/sync_uploads.sh (100%) diff --git a/backend/.dockerignore b/backend/.dockerignore index 31f5b28f3..25a941824 100644 --- a/backend/.dockerignore +++ b/backend/.dockerignore @@ -15,7 +15,7 @@ node_modules/ scripts/ dist/ -db-migration-worker/ +maintenance-worker/ neo4j/ public/uploads/* diff --git a/backend/db-migration-worker/.ssh/known_hosts b/backend/db-migration-worker/.ssh/known_hosts deleted file mode 100644 index 947840cb2..000000000 --- a/backend/db-migration-worker/.ssh/known_hosts +++ /dev/null @@ -1,3 +0,0 @@ -|1|GuOYlVEhTowidPs18zj9p5F2j3o=|sDHJYLz9Ftv11oXeGEjs7SpVyg0= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBM5N29bI5CeKu1/RBPyM2fwyf7fuajOO+tyhKe1+CC2sZ1XNB5Ff6t6MtCLNRv2mUuvzTbW/HkisDiA5tuXUHOk= -|1|2KP9NV+Q5g2MrtjAeFSVcs8YeOI=|nf3h4wWVwC4xbBS1kzgzE2tBldk= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNhRK6BeIEUxXlS0z/pOfkUkSPfn33g4J1U3L+MyUQYHm+7agT08799ANJhnvELKE1tt4Vx80I9UR81oxzZcy3E= -|1|HonYIRNhKyroUHPKU1HSZw0+Qzs=|5T1btfwFBz2vNSldhqAIfTbfIgQ= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNhRK6BeIEUxXlS0z/pOfkUkSPfn33g4J1U3L+MyUQYHm+7agT08799ANJhnvELKE1tt4Vx80I9UR81oxzZcy3E= diff --git a/backend/docker-compose.db-migration.yml b/backend/docker-compose.maintenance.yml similarity index 82% rename from backend/docker-compose.db-migration.yml rename to backend/docker-compose.maintenance.yml index 02f054d1b..943540351 100644 --- a/backend/docker-compose.db-migration.yml +++ b/backend/docker-compose.maintenance.yml @@ -9,14 +9,14 @@ services: - mongo-export:/mongo-export environment: - NEO4J_apoc_import_file_enabled=true - db-migration-worker: + maintenance-worker: build: - context: db-migration-worker + context: maintenance-worker volumes: - mongo-export:/mongo-export - uploads:/uploads - - ./db-migration-worker/migration/:/migration - - ./db-migration-worker/.ssh/:/root/.ssh/ + - ./maintenance-worker/migration/:/migration + - ./maintenance-worker/.ssh/:/root/.ssh/ networks: - hc-network depends_on: diff --git a/backend/db-migration-worker/.dockerignore b/backend/maintenance-worker/.dockerignore similarity index 100% rename from backend/db-migration-worker/.dockerignore rename to backend/maintenance-worker/.dockerignore diff --git a/backend/db-migration-worker/.gitignore b/backend/maintenance-worker/.gitignore similarity index 100% rename from backend/db-migration-worker/.gitignore rename to backend/maintenance-worker/.gitignore diff --git a/backend/db-migration-worker/Dockerfile b/backend/maintenance-worker/Dockerfile similarity index 100% rename from backend/db-migration-worker/Dockerfile rename to backend/maintenance-worker/Dockerfile diff --git a/backend/db-migration-worker/README.md b/backend/maintenance-worker/README.md similarity index 96% rename from backend/db-migration-worker/README.md rename to backend/maintenance-worker/README.md index 3d0f86edd..4b93686ac 100644 --- a/backend/db-migration-worker/README.md +++ b/backend/maintenance-worker/README.md @@ -29,7 +29,7 @@ SSH_USERNAME=username SSH_HOST=some.server.com MONGODB_USERNAME='hc-api' MONGODB Download the remote mongo database: ```bash -docker-compose exec db-migration-worker ./import.sh +docker-compose exec maintenance-worker ./import.sh ``` Import the local download into Neo4J: diff --git a/backend/db-migration-worker/migrate.sh b/backend/maintenance-worker/migrate.sh similarity index 100% rename from backend/db-migration-worker/migrate.sh rename to backend/maintenance-worker/migrate.sh diff --git a/backend/db-migration-worker/migration/mongo/import.sh b/backend/maintenance-worker/migration/mongo/import.sh similarity index 100% rename from backend/db-migration-worker/migration/mongo/import.sh rename to backend/maintenance-worker/migration/mongo/import.sh diff --git a/backend/db-migration-worker/migration/neo4j/badges.cql b/backend/maintenance-worker/migration/neo4j/badges.cql similarity index 100% rename from backend/db-migration-worker/migration/neo4j/badges.cql rename to backend/maintenance-worker/migration/neo4j/badges.cql diff --git a/backend/db-migration-worker/migration/neo4j/categories.cql b/backend/maintenance-worker/migration/neo4j/categories.cql similarity index 100% rename from backend/db-migration-worker/migration/neo4j/categories.cql rename to backend/maintenance-worker/migration/neo4j/categories.cql diff --git a/backend/db-migration-worker/migration/neo4j/comments.cql b/backend/maintenance-worker/migration/neo4j/comments.cql similarity index 100% rename from backend/db-migration-worker/migration/neo4j/comments.cql rename to backend/maintenance-worker/migration/neo4j/comments.cql diff --git a/backend/db-migration-worker/migration/neo4j/contributions.cql b/backend/maintenance-worker/migration/neo4j/contributions.cql similarity index 100% rename from backend/db-migration-worker/migration/neo4j/contributions.cql rename to backend/maintenance-worker/migration/neo4j/contributions.cql diff --git a/backend/db-migration-worker/migration/neo4j/follows.cql b/backend/maintenance-worker/migration/neo4j/follows.cql similarity index 100% rename from backend/db-migration-worker/migration/neo4j/follows.cql rename to backend/maintenance-worker/migration/neo4j/follows.cql diff --git a/backend/db-migration-worker/migration/neo4j/import.sh b/backend/maintenance-worker/migration/neo4j/import.sh similarity index 100% rename from backend/db-migration-worker/migration/neo4j/import.sh rename to backend/maintenance-worker/migration/neo4j/import.sh diff --git a/backend/db-migration-worker/migration/neo4j/shouts.cql b/backend/maintenance-worker/migration/neo4j/shouts.cql similarity index 100% rename from backend/db-migration-worker/migration/neo4j/shouts.cql rename to backend/maintenance-worker/migration/neo4j/shouts.cql diff --git a/backend/db-migration-worker/migration/neo4j/users.cql b/backend/maintenance-worker/migration/neo4j/users.cql similarity index 100% rename from backend/db-migration-worker/migration/neo4j/users.cql rename to backend/maintenance-worker/migration/neo4j/users.cql diff --git a/backend/db-migration-worker/sync_uploads.sh b/backend/maintenance-worker/sync_uploads.sh similarity index 100% rename from backend/db-migration-worker/sync_uploads.sh rename to backend/maintenance-worker/sync_uploads.sh diff --git a/deployment/db-migration-worker.yaml b/deployment/db-migration-worker.yaml index 55743e360..8ee86f11c 100644 --- a/deployment/db-migration-worker.yaml +++ b/deployment/db-migration-worker.yaml @@ -2,7 +2,7 @@ kind: Pod apiVersion: v1 metadata: - name: nitro-db-migration-worker + name: nitro-maintenance-worker namespace: human-connection spec: volumes: @@ -14,11 +14,11 @@ persistentVolumeClaim: claimName: mongo-export-claim containers: - - name: nitro-db-migration-worker - image: humanconnection/db-migration-worker:latest + - name: nitro-maintenance-worker + image: humanconnection/maintenance-worker:latest envFrom: - configMapRef: - name: db-migration-worker + name: maintenance-worker volumeMounts: - name: secret-volume readOnly: false diff --git a/deployment/legacy-migration/deployment-backend.yaml b/deployment/legacy-migration/deployment-backend.yaml index 1adeb0665..1bcb380aa 100644 --- a/deployment/legacy-migration/deployment-backend.yaml +++ b/deployment/legacy-migration/deployment-backend.yaml @@ -8,12 +8,12 @@ template: spec: containers: - - name: nitro-db-migration-worker - image: humanconnection/db-migration-worker:latest + - name: nitro-maintenance-worker + image: humanconnection/maintenance-worker:latest imagePullPolicy: Always envFrom: - configMapRef: - name: db-migration-worker + name: maintenance-worker volumeMounts: - name: secret-volume readOnly: false diff --git a/deployment/legacy-migration/deployment-neo4j.yaml b/deployment/legacy-migration/deployment-neo4j.yaml index 2852b90cb..0f3252984 100644 --- a/deployment/legacy-migration/deployment-neo4j.yaml +++ b/deployment/legacy-migration/deployment-neo4j.yaml @@ -8,12 +8,12 @@ template: spec: containers: - - name: nitro-db-migration-worker - image: humanconnection/db-migration-worker:latest + - name: nitro-maintenance-worker + image: humanconnection/maintenance-worker:latest imagePullPolicy: Always envFrom: - configMapRef: - name: db-migration-worker + name: maintenance-worker env: - name: COMMIT value: From 03521547429a8c4c7dae726b106c1b552eedc408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Thu, 18 Apr 2019 22:17:41 +0200 Subject: [PATCH 02/24] Removing unused docker-compose.yml in backend/ --- backend/docker-compose.cypress.yml | 18 ---------- backend/docker-compose.override.yml | 23 ------------- backend/docker-compose.travis.yml | 14 -------- backend/docker-compose.yml | 34 ------------------- ...ance.yml => docker-compose.maintenance.yml | 10 +++--- 5 files changed, 5 insertions(+), 94 deletions(-) delete mode 100644 backend/docker-compose.cypress.yml delete mode 100644 backend/docker-compose.override.yml delete mode 100644 backend/docker-compose.travis.yml delete mode 100644 backend/docker-compose.yml rename backend/docker-compose.maintenance.yml => docker-compose.maintenance.yml (79%) diff --git a/backend/docker-compose.cypress.yml b/backend/docker-compose.cypress.yml deleted file mode 100644 index 3d577e638..000000000 --- a/backend/docker-compose.cypress.yml +++ /dev/null @@ -1,18 +0,0 @@ -version: "3.7" - -services: - neo4j: - environment: - - NEO4J_AUTH=none - ports: - - 7687:7687 - - 7474:7474 - backend: - ports: - - 4001:4001 - - 4123:4123 - image: humanconnection/nitro-backend:builder - build: - context: . - target: builder - command: yarn run test:cypress diff --git a/backend/docker-compose.override.yml b/backend/docker-compose.override.yml deleted file mode 100644 index b972c31f6..000000000 --- a/backend/docker-compose.override.yml +++ /dev/null @@ -1,23 +0,0 @@ -version: "3.7" - -services: - backend: - image: humanconnection/nitro-backend:builder - build: - context: . - target: builder - volumes: - - .:/nitro-backend - - /nitro-backend/node_modules - command: yarn run dev - neo4j: - environment: - - NEO4J_AUTH=none - ports: - - 7687:7687 - - 7474:7474 - volumes: - - neo4j-data:/data - -volumes: - neo4j-data: diff --git a/backend/docker-compose.travis.yml b/backend/docker-compose.travis.yml deleted file mode 100644 index e1998f6dd..000000000 --- a/backend/docker-compose.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -version: "3.7" - -services: - neo4j: - environment: - - NEO4J_AUTH=none - ports: - - 7687:7687 - - 7474:7474 - backend: - image: humanconnection/nitro-backend:builder - build: - context: . - target: builder diff --git a/backend/docker-compose.yml b/backend/docker-compose.yml deleted file mode 100644 index 30d102f96..000000000 --- a/backend/docker-compose.yml +++ /dev/null @@ -1,34 +0,0 @@ -version: "3.7" - -services: - backend: - image: humanconnection/nitro-backend:latest - build: - context: . - target: production - networks: - - hc-network - depends_on: - - neo4j - ports: - - 4000:4000 - environment: - - NEO4J_URI=bolt://neo4j:7687 - - GRAPHQL_PORT=4000 - - GRAPHQL_URI=http://localhost:4000 - - CLIENT_URI=http://localhost:3000 - - JWT_SECRET=b/&&7b78BF&fv/Vd - - MOCK=false - - MAPBOX_TOKEN=pk.eyJ1IjoiaHVtYW4tY29ubmVjdGlvbiIsImEiOiJjajl0cnBubGoweTVlM3VwZ2lzNTNud3ZtIn0.KZ8KK9l70omjXbEkkbHGsQ - - PRIVATE_KEY_PASSPHRASE=a7dsf78sadg87ad87sfagsadg78 - - neo4j: - image: humanconnection/neo4j:latest - build: - context: neo4j - networks: - - hc-network - -networks: - hc-network: - name: hc-network diff --git a/backend/docker-compose.maintenance.yml b/docker-compose.maintenance.yml similarity index 79% rename from backend/docker-compose.maintenance.yml rename to docker-compose.maintenance.yml index 943540351..fbbe4ef41 100644 --- a/backend/docker-compose.maintenance.yml +++ b/docker-compose.maintenance.yml @@ -1,4 +1,4 @@ -version: "3.7" +version: "3.4" services: backend: @@ -9,14 +9,14 @@ services: - mongo-export:/mongo-export environment: - NEO4J_apoc_import_file_enabled=true - maintenance-worker: + maintenance: build: - context: maintenance-worker + context: backend/maintenance-worker volumes: - mongo-export:/mongo-export - uploads:/uploads - - ./maintenance-worker/migration/:/migration - - ./maintenance-worker/.ssh/:/root/.ssh/ + - ./backend/maintenance-worker/migration/:/migration + - ./backend/maintenance-worker/.ssh/:/root/.ssh/ networks: - hc-network depends_on: From c7a08b792ed03dafd1888352f22758789a7dfc7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Thu, 18 Apr 2019 22:40:51 +0200 Subject: [PATCH 03/24] Move maintenance & neo4j in dedicated folders --- docker-compose.maintenance.yml | 6 +++--- docker-compose.yml | 2 +- {backend/maintenance-worker => maintenance}/.dockerignore | 0 {backend/maintenance-worker => maintenance}/.gitignore | 0 {backend/maintenance-worker => maintenance}/Dockerfile | 0 {backend/maintenance-worker => maintenance}/README.md | 0 {backend/maintenance-worker => maintenance}/migrate.sh | 0 .../migration/mongo/import.sh | 0 .../migration/neo4j/badges.cql | 0 .../migration/neo4j/categories.cql | 0 .../migration/neo4j/comments.cql | 0 .../migration/neo4j/contributions.cql | 0 .../migration/neo4j/follows.cql | 0 .../migration/neo4j/import.sh | 0 .../migration/neo4j/shouts.cql | 0 .../migration/neo4j/users.cql | 0 {backend/maintenance-worker => maintenance}/sync_uploads.sh | 0 {backend/neo4j => neo4j}/Dockerfile | 0 {backend/neo4j => neo4j}/migrate.sh | 0 19 files changed, 4 insertions(+), 4 deletions(-) rename {backend/maintenance-worker => maintenance}/.dockerignore (100%) rename {backend/maintenance-worker => maintenance}/.gitignore (100%) rename {backend/maintenance-worker => maintenance}/Dockerfile (100%) rename {backend/maintenance-worker => maintenance}/README.md (100%) rename {backend/maintenance-worker => maintenance}/migrate.sh (100%) rename {backend/maintenance-worker => maintenance}/migration/mongo/import.sh (100%) rename {backend/maintenance-worker => maintenance}/migration/neo4j/badges.cql (100%) rename {backend/maintenance-worker => maintenance}/migration/neo4j/categories.cql (100%) rename {backend/maintenance-worker => maintenance}/migration/neo4j/comments.cql (100%) rename {backend/maintenance-worker => maintenance}/migration/neo4j/contributions.cql (100%) rename {backend/maintenance-worker => maintenance}/migration/neo4j/follows.cql (100%) rename {backend/maintenance-worker => maintenance}/migration/neo4j/import.sh (100%) rename {backend/maintenance-worker => maintenance}/migration/neo4j/shouts.cql (100%) rename {backend/maintenance-worker => maintenance}/migration/neo4j/users.cql (100%) rename {backend/maintenance-worker => maintenance}/sync_uploads.sh (100%) rename {backend/neo4j => neo4j}/Dockerfile (100%) rename {backend/neo4j => neo4j}/migrate.sh (100%) diff --git a/docker-compose.maintenance.yml b/docker-compose.maintenance.yml index fbbe4ef41..c09accd1d 100644 --- a/docker-compose.maintenance.yml +++ b/docker-compose.maintenance.yml @@ -11,12 +11,12 @@ services: - NEO4J_apoc_import_file_enabled=true maintenance: build: - context: backend/maintenance-worker + context: maintenance volumes: - mongo-export:/mongo-export - uploads:/uploads - - ./backend/maintenance-worker/migration/:/migration - - ./backend/maintenance-worker/.ssh/:/root/.ssh/ + - ./maintenance/migration/:/migration + - ./maintenance/.ssh/:/root/.ssh/ networks: - hc-network depends_on: diff --git a/docker-compose.yml b/docker-compose.yml index a7e7c0802..896d1bef9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -38,7 +38,7 @@ services: neo4j: image: humanconnection/neo4j:latest build: - context: backend/neo4j + context: neo4j networks: - hc-network diff --git a/backend/maintenance-worker/.dockerignore b/maintenance/.dockerignore similarity index 100% rename from backend/maintenance-worker/.dockerignore rename to maintenance/.dockerignore diff --git a/backend/maintenance-worker/.gitignore b/maintenance/.gitignore similarity index 100% rename from backend/maintenance-worker/.gitignore rename to maintenance/.gitignore diff --git a/backend/maintenance-worker/Dockerfile b/maintenance/Dockerfile similarity index 100% rename from backend/maintenance-worker/Dockerfile rename to maintenance/Dockerfile diff --git a/backend/maintenance-worker/README.md b/maintenance/README.md similarity index 100% rename from backend/maintenance-worker/README.md rename to maintenance/README.md diff --git a/backend/maintenance-worker/migrate.sh b/maintenance/migrate.sh similarity index 100% rename from backend/maintenance-worker/migrate.sh rename to maintenance/migrate.sh diff --git a/backend/maintenance-worker/migration/mongo/import.sh b/maintenance/migration/mongo/import.sh similarity index 100% rename from backend/maintenance-worker/migration/mongo/import.sh rename to maintenance/migration/mongo/import.sh diff --git a/backend/maintenance-worker/migration/neo4j/badges.cql b/maintenance/migration/neo4j/badges.cql similarity index 100% rename from backend/maintenance-worker/migration/neo4j/badges.cql rename to maintenance/migration/neo4j/badges.cql diff --git a/backend/maintenance-worker/migration/neo4j/categories.cql b/maintenance/migration/neo4j/categories.cql similarity index 100% rename from backend/maintenance-worker/migration/neo4j/categories.cql rename to maintenance/migration/neo4j/categories.cql diff --git a/backend/maintenance-worker/migration/neo4j/comments.cql b/maintenance/migration/neo4j/comments.cql similarity index 100% rename from backend/maintenance-worker/migration/neo4j/comments.cql rename to maintenance/migration/neo4j/comments.cql diff --git a/backend/maintenance-worker/migration/neo4j/contributions.cql b/maintenance/migration/neo4j/contributions.cql similarity index 100% rename from backend/maintenance-worker/migration/neo4j/contributions.cql rename to maintenance/migration/neo4j/contributions.cql diff --git a/backend/maintenance-worker/migration/neo4j/follows.cql b/maintenance/migration/neo4j/follows.cql similarity index 100% rename from backend/maintenance-worker/migration/neo4j/follows.cql rename to maintenance/migration/neo4j/follows.cql diff --git a/backend/maintenance-worker/migration/neo4j/import.sh b/maintenance/migration/neo4j/import.sh similarity index 100% rename from backend/maintenance-worker/migration/neo4j/import.sh rename to maintenance/migration/neo4j/import.sh diff --git a/backend/maintenance-worker/migration/neo4j/shouts.cql b/maintenance/migration/neo4j/shouts.cql similarity index 100% rename from backend/maintenance-worker/migration/neo4j/shouts.cql rename to maintenance/migration/neo4j/shouts.cql diff --git a/backend/maintenance-worker/migration/neo4j/users.cql b/maintenance/migration/neo4j/users.cql similarity index 100% rename from backend/maintenance-worker/migration/neo4j/users.cql rename to maintenance/migration/neo4j/users.cql diff --git a/backend/maintenance-worker/sync_uploads.sh b/maintenance/sync_uploads.sh similarity index 100% rename from backend/maintenance-worker/sync_uploads.sh rename to maintenance/sync_uploads.sh diff --git a/backend/neo4j/Dockerfile b/neo4j/Dockerfile similarity index 100% rename from backend/neo4j/Dockerfile rename to neo4j/Dockerfile diff --git a/backend/neo4j/migrate.sh b/neo4j/migrate.sh similarity index 100% rename from backend/neo4j/migrate.sh rename to neo4j/migrate.sh From be9980f2aa1ccacf321f4e2cc7c3499b42054fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Thu, 18 Apr 2019 22:58:04 +0200 Subject: [PATCH 04/24] Tag maintenance image consitently --- docker-compose.maintenance.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.maintenance.yml b/docker-compose.maintenance.yml index c09accd1d..6cfaf1f46 100644 --- a/docker-compose.maintenance.yml +++ b/docker-compose.maintenance.yml @@ -10,6 +10,7 @@ services: environment: - NEO4J_apoc_import_file_enabled=true maintenance: + image: humanconnection/maintenance-worker:latest build: context: maintenance volumes: From 45c0dbd5a87a669e87dd1525326b42544e9bee1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Fri, 19 Apr 2019 01:23:51 +0200 Subject: [PATCH 05/24] Provision maintenance-worker to seed/reset db --- docker-compose.maintenance.yml | 7 +++++++ maintenance/Dockerfile | 29 ++++++++++++++++++++--------- maintenance/seeding/.env.template | 12 ++++++++++++ maintenance/seeding/package.json | 12 ++++++++++++ 4 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 maintenance/seeding/.env.template create mode 100644 maintenance/seeding/package.json diff --git a/docker-compose.maintenance.yml b/docker-compose.maintenance.yml index 6cfaf1f46..d98cbab2e 100644 --- a/docker-compose.maintenance.yml +++ b/docker-compose.maintenance.yml @@ -23,6 +23,13 @@ services: depends_on: - backend environment: + - GRAPHQL_PORT=4000 + - GRAPHQL_URI=http://localhost:4000 + - CLIENT_URI=http://localhost:3000 + - JWT_SECRET=b/&&7b78BF&fv/Vd + - MOCK=false + - MAPBOX_TOKEN=pk.eyJ1IjoiaHVtYW4tY29ubmVjdGlvbiIsImEiOiJjajl0cnBubGoweTVlM3VwZ2lzNTNud3ZtIn0.KZ8KK9l70omjXbEkkbHGsQ + - PRIVATE_KEY_PASSPHRASE=a7dsf78sadg87ad87sfagsadg78 - NEO4J_URI=bolt://neo4j:7687 - "SSH_USERNAME=${SSH_USERNAME}" - "SSH_HOST=${SSH_HOST}" diff --git a/maintenance/Dockerfile b/maintenance/Dockerfile index 865a4c330..8cc3d6c28 100644 --- a/maintenance/Dockerfile +++ b/maintenance/Dockerfile @@ -1,13 +1,24 @@ -FROM mongo:4 +FROM humanconnection/neo4j:latest as base + +RUN apk upgrade --update +RUN apk add mongodb mongodb-tools openssh nodejs yarn -RUN apt-get update && apt-get -y install --no-install-recommends wget apt-transport-https \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* -RUN wget -O - https://debian.neo4j.org/neotechnology.gpg.key | apt-key add - -RUN echo 'deb https://debian.neo4j.org/repo stable/' | tee /etc/apt/sources.list.d/neo4j.list -RUN apt-get update && apt-get -y install --no-install-recommends openjdk-8-jre openssh-client neo4j rsync \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* COPY migration ./migration COPY migrate.sh /usr/local/bin/migrate COPY sync_uploads.sh /usr/local/bin/sync_uploads + + +ENV NODE_ENV=maintenance +FROM humanconnection/nitro-backend:latest as backend +FROM base +COPY --from=backend /nitro-backend /nitro-backend + +COPY seeding/package.json . +# Install graphql-request manually, it's required for seeding and not included +# in backend's preinstalled node_modules/ +RUN yarn install && \ + cp -r ./node_modules/* /nitro-backend/node_modules/ && \ + rm -rf ./node_modules/ yarn.lock && \ + mv package.json /nitro-backend/package.json +# We have to do this odd copying to prevent cleaning `node_modules` folder which +# would happen if we `yarn install|add` in the target directory diff --git a/maintenance/seeding/.env.template b/maintenance/seeding/.env.template new file mode 100644 index 000000000..abc62b2dc --- /dev/null +++ b/maintenance/seeding/.env.template @@ -0,0 +1,12 @@ +NEO4J_URI=bolt://localhost:7687 +NEO4J_USER=neo4j +NEO4J_PASSWORD=letmein +GRAPHQL_PORT=4000 +GRAPHQL_URI=http://localhost:4000 +CLIENT_URI=http://localhost:3000 +MOCK=false + +JWT_SECRET="b/&&7b78BF&fv/Vd" +MAPBOX_TOKEN="pk.eyJ1IjoiaHVtYW4tY29ubmVjdGlvbiIsImEiOiJjajl0cnBubGoweTVlM3VwZ2lzNTNud3ZtIn0.KZ8KK9l70omjXbEkkbHGsQ" + +PRIVATE_KEY_PASSPHRASE="a7dsf78sadg87ad87sfagsadg78" diff --git a/maintenance/seeding/package.json b/maintenance/seeding/package.json new file mode 100644 index 000000000..f3e5c115f --- /dev/null +++ b/maintenance/seeding/package.json @@ -0,0 +1,12 @@ +{ + "license": "MIT", + "scripts": { + "start": "node dist/", + "db:script:seed": "wait-on tcp:4001 && node dist/seed/seed-db.js", + "db:reset": "node dist/seed/reset-db.js", + "db:seed": "cross-env GRAPHQL_URI=http://localhost:4001 GRAPHQL_PORT=4001 DISABLED_MIDDLEWARES=permissions run-p --race start db:script:seed" + }, + "dependencies": { + "graphql-request": "*" + } +} From 542c37a32bb1761cd5f9380d07c823da48ca061f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Fri, 19 Apr 2019 14:06:15 +0200 Subject: [PATCH 06/24] Make maintenance-worker independent of backend --- docker-compose.maintenance.yml | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/docker-compose.maintenance.yml b/docker-compose.maintenance.yml index d98cbab2e..d88e477b3 100644 --- a/docker-compose.maintenance.yml +++ b/docker-compose.maintenance.yml @@ -1,14 +1,6 @@ version: "3.4" services: - backend: - volumes: - - uploads:/nitro-backend/public/uploads - neo4j: - volumes: - - mongo-export:/mongo-export - environment: - - NEO4J_apoc_import_file_enabled=true maintenance: image: humanconnection/maintenance-worker:latest build: @@ -18,10 +10,9 @@ services: - uploads:/uploads - ./maintenance/migration/:/migration - ./maintenance/.ssh/:/root/.ssh/ + - neo4j-data:/data networks: - hc-network - depends_on: - - backend environment: - GRAPHQL_PORT=4000 - GRAPHQL_URI=http://localhost:4000 @@ -30,7 +21,7 @@ services: - MOCK=false - MAPBOX_TOKEN=pk.eyJ1IjoiaHVtYW4tY29ubmVjdGlvbiIsImEiOiJjajl0cnBubGoweTVlM3VwZ2lzNTNud3ZtIn0.KZ8KK9l70omjXbEkkbHGsQ - PRIVATE_KEY_PASSPHRASE=a7dsf78sadg87ad87sfagsadg78 - - NEO4J_URI=bolt://neo4j:7687 + - NEO4J_URI=bolt://localhost:7687 - "SSH_USERNAME=${SSH_USERNAME}" - "SSH_HOST=${SSH_HOST}" - "MONGODB_USERNAME=${MONGODB_USERNAME}" @@ -38,7 +29,15 @@ services: - "MONGODB_AUTH_DB=${MONGODB_AUTH_DB}" - "MONGODB_DATABASE=${MONGODB_DATABASE}" - "UPLOADS_DIRECTORY=${UPLOADS_DIRECTORY}" + - NEO4J_AUTH=none + ports: + - 7687:7687 + - 7474:7474 volumes: mongo-export: uploads: + neo4j-data: + +networks: + hc-network: From af4f391e28b7bbbe1bc652360c32bc01a2ba8dfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Fri, 19 Apr 2019 15:27:48 +0200 Subject: [PATCH 07/24] Simplify migration script --- maintenance/Dockerfile | 25 ++++++++++++---------- maintenance/{ => binaries}/migrate.sh | 0 maintenance/{ => binaries}/sync_uploads.sh | 0 maintenance/migration/mongo/import.sh | 10 ++++----- 4 files changed, 18 insertions(+), 17 deletions(-) rename maintenance/{ => binaries}/migrate.sh (100%) rename maintenance/{ => binaries}/sync_uploads.sh (100%) diff --git a/maintenance/Dockerfile b/maintenance/Dockerfile index 8cc3d6c28..37985fefa 100644 --- a/maintenance/Dockerfile +++ b/maintenance/Dockerfile @@ -1,18 +1,17 @@ FROM humanconnection/neo4j:latest as base -RUN apk upgrade --update -RUN apk add mongodb mongodb-tools openssh nodejs yarn - -COPY migration ./migration -COPY migrate.sh /usr/local/bin/migrate -COPY sync_uploads.sh /usr/local/bin/sync_uploads - - ENV NODE_ENV=maintenance -FROM humanconnection/nitro-backend:latest as backend -FROM base -COPY --from=backend /nitro-backend /nitro-backend +EXPOSE 7687 7474 +VOLUME /mongo-export /uploads /data +RUN apk upgrade --update +RUN apk add --no-cache mongodb-tools openssh nodejs yarn + +FROM humanconnection/nitro-backend:latest as backend + +FROM base + +COPY --from=backend /nitro-backend /nitro-backend COPY seeding/package.json . # Install graphql-request manually, it's required for seeding and not included # in backend's preinstalled node_modules/ @@ -22,3 +21,7 @@ RUN yarn install && \ mv package.json /nitro-backend/package.json # We have to do this odd copying to prevent cleaning `node_modules` folder which # would happen if we `yarn install|add` in the target directory + +COPY migration ./migration +COPY ./binaries/migrate.sh /usr/local/bin/migrate +COPY ./binaries/sync_uploads.sh /usr/local/bin/sync_uploads diff --git a/maintenance/migrate.sh b/maintenance/binaries/migrate.sh similarity index 100% rename from maintenance/migrate.sh rename to maintenance/binaries/migrate.sh diff --git a/maintenance/sync_uploads.sh b/maintenance/binaries/sync_uploads.sh similarity index 100% rename from maintenance/sync_uploads.sh rename to maintenance/binaries/sync_uploads.sh diff --git a/maintenance/migration/mongo/import.sh b/maintenance/migration/mongo/import.sh index 7cf3e91e4..15732e589 100755 --- a/maintenance/migration/mongo/import.sh +++ b/maintenance/migration/mongo/import.sh @@ -9,16 +9,14 @@ echo "MONGODB_DATABASE ${MONGODB_DATABASE}" echo "MONGODB_AUTH_DB ${MONGODB_AUTH_DB}" echo "-------------------------------------------------" -mongo ${MONGODB_DATABASE} --eval "db.dropDatabase();" rm -rf /mongo-export/* ssh -4 -M -S my-ctrl-socket -fnNT -L 27018:localhost:27017 -l ${SSH_USERNAME} ${SSH_HOST} -mongodump --host localhost -d ${MONGODB_DATABASE} --port 27018 --username ${MONGODB_USERNAME} --password ${MONGODB_PASSWORD} --authenticationDatabase ${MONGODB_AUTH_DB} --gzip --archive=/tmp/mongodump.archive -mongorestore --gzip --archive=/tmp/mongodump.archive -ssh -S my-ctrl-socket -O check -l ${SSH_USERNAME} ${SSH_HOST} -ssh -S my-ctrl-socket -O exit -l ${SSH_USERNAME} ${SSH_HOST} for collection in "categories" "badges" "users" "contributions" "comments" "follows" "shouts" do - mongoexport --db ${MONGODB_DATABASE} --collection $collection --out "/mongo-export/$collection.json" + mongoexport --host localhost -d ${MONGODB_DATABASE} --port 27018 --username ${MONGODB_USERNAME} --password ${MONGODB_PASSWORD} --authenticationDatabase ${MONGODB_AUTH_DB} --db ${MONGODB_DATABASE} --collection $collection --out "/mongo-export/$collection.json" done + +ssh -S my-ctrl-socket -O check -l ${SSH_USERNAME} ${SSH_HOST} +ssh -S my-ctrl-socket -O exit -l ${SSH_USERNAME} ${SSH_HOST} From a5f842632548f2607a8d68c32a613282e28b4a79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Fri, 19 Apr 2019 15:39:20 +0200 Subject: [PATCH 08/24] Add missing env, import mongo collections to neo4j --- docker-compose.maintenance.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker-compose.maintenance.yml b/docker-compose.maintenance.yml index d88e477b3..aca042a54 100644 --- a/docker-compose.maintenance.yml +++ b/docker-compose.maintenance.yml @@ -22,6 +22,8 @@ services: - MAPBOX_TOKEN=pk.eyJ1IjoiaHVtYW4tY29ubmVjdGlvbiIsImEiOiJjajl0cnBubGoweTVlM3VwZ2lzNTNud3ZtIn0.KZ8KK9l70omjXbEkkbHGsQ - PRIVATE_KEY_PASSPHRASE=a7dsf78sadg87ad87sfagsadg78 - NEO4J_URI=bolt://localhost:7687 + - NEO4J_apoc_import_file_enabled=true + - NEO4J_AUTH=none - "SSH_USERNAME=${SSH_USERNAME}" - "SSH_HOST=${SSH_HOST}" - "MONGODB_USERNAME=${MONGODB_USERNAME}" @@ -29,7 +31,6 @@ services: - "MONGODB_AUTH_DB=${MONGODB_AUTH_DB}" - "MONGODB_DATABASE=${MONGODB_DATABASE}" - "UPLOADS_DIRECTORY=${UPLOADS_DIRECTORY}" - - NEO4J_AUTH=none ports: - 7687:7687 - 7474:7474 From abc17b2adcbf939013e9693f012b78271932e690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Fri, 19 Apr 2019 15:51:30 +0200 Subject: [PATCH 09/24] Fix import_legacy_uploads --- maintenance/Dockerfile | 5 ++--- maintenance/binaries/{migrate.sh => import_legacy_db} | 0 .../binaries/{sync_uploads.sh => import_legacy_uploads} | 0 maintenance/binaries/reset_db | 4 ++++ maintenance/binaries/seed_db | 4 ++++ 5 files changed, 10 insertions(+), 3 deletions(-) rename maintenance/binaries/{migrate.sh => import_legacy_db} (100%) rename maintenance/binaries/{sync_uploads.sh => import_legacy_uploads} (100%) create mode 100755 maintenance/binaries/reset_db create mode 100755 maintenance/binaries/seed_db diff --git a/maintenance/Dockerfile b/maintenance/Dockerfile index 37985fefa..83154d836 100644 --- a/maintenance/Dockerfile +++ b/maintenance/Dockerfile @@ -5,7 +5,7 @@ EXPOSE 7687 7474 VOLUME /mongo-export /uploads /data RUN apk upgrade --update -RUN apk add --no-cache mongodb-tools openssh nodejs yarn +RUN apk add --no-cache mongodb-tools openssh nodejs yarn rsync FROM humanconnection/nitro-backend:latest as backend @@ -23,5 +23,4 @@ RUN yarn install && \ # would happen if we `yarn install|add` in the target directory COPY migration ./migration -COPY ./binaries/migrate.sh /usr/local/bin/migrate -COPY ./binaries/sync_uploads.sh /usr/local/bin/sync_uploads +COPY ./binaries/* /usr/local/bin/ diff --git a/maintenance/binaries/migrate.sh b/maintenance/binaries/import_legacy_db similarity index 100% rename from maintenance/binaries/migrate.sh rename to maintenance/binaries/import_legacy_db diff --git a/maintenance/binaries/sync_uploads.sh b/maintenance/binaries/import_legacy_uploads similarity index 100% rename from maintenance/binaries/sync_uploads.sh rename to maintenance/binaries/import_legacy_uploads diff --git a/maintenance/binaries/reset_db b/maintenance/binaries/reset_db new file mode 100755 index 000000000..ce768089a --- /dev/null +++ b/maintenance/binaries/reset_db @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -e + +cd /nitro-backend && yarn run db:reset diff --git a/maintenance/binaries/seed_db b/maintenance/binaries/seed_db new file mode 100755 index 000000000..ce768089a --- /dev/null +++ b/maintenance/binaries/seed_db @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -e + +cd /nitro-backend && yarn run db:reset From 56c0f8dfdca5eea31c7bad661cd1a493609db89d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Fri, 19 Apr 2019 16:17:25 +0200 Subject: [PATCH 10/24] Create private SSH key from base64 encoded env --- docker-compose.maintenance.yml | 4 ++-- maintenance/binaries/create_private_ssh_key_from_env | 6 ++++++ maintenance/binaries/import_legacy_uploads | 1 + maintenance/migration/mongo/import.sh | 2 ++ 4 files changed, 11 insertions(+), 2 deletions(-) create mode 100755 maintenance/binaries/create_private_ssh_key_from_env diff --git a/docker-compose.maintenance.yml b/docker-compose.maintenance.yml index aca042a54..b18f97513 100644 --- a/docker-compose.maintenance.yml +++ b/docker-compose.maintenance.yml @@ -8,9 +8,8 @@ services: volumes: - mongo-export:/mongo-export - uploads:/uploads - - ./maintenance/migration/:/migration - - ./maintenance/.ssh/:/root/.ssh/ - neo4j-data:/data + - ./maintenance/migration/:/migration networks: - hc-network environment: @@ -26,6 +25,7 @@ services: - NEO4J_AUTH=none - "SSH_USERNAME=${SSH_USERNAME}" - "SSH_HOST=${SSH_HOST}" + - "SSH_PRIVATE_KEY=${SSH_PRIVATE_KEY}" - "MONGODB_USERNAME=${MONGODB_USERNAME}" - "MONGODB_PASSWORD=${MONGODB_PASSWORD}" - "MONGODB_AUTH_DB=${MONGODB_AUTH_DB}" diff --git a/maintenance/binaries/create_private_ssh_key_from_env b/maintenance/binaries/create_private_ssh_key_from_env new file mode 100755 index 000000000..f44671978 --- /dev/null +++ b/maintenance/binaries/create_private_ssh_key_from_env @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -e + +mkdir -p ~/.ssh +echo $SSH_PRIVATE_KEY | base64 -d > ~/.ssh/id_rsa +chmod 600 ~/.ssh/id_rsa diff --git a/maintenance/binaries/import_legacy_uploads b/maintenance/binaries/import_legacy_uploads index d24936e3b..73240715b 100755 --- a/maintenance/binaries/import_legacy_uploads +++ b/maintenance/binaries/import_legacy_uploads @@ -9,4 +9,5 @@ do fi done +create_private_ssh_key_from_env rsync --archive --update --verbose ${SSH_USERNAME}@${SSH_HOST}:${UPLOADS_DIRECTORY}/* /uploads/ diff --git a/maintenance/migration/mongo/import.sh b/maintenance/migration/mongo/import.sh index 15732e589..89c255a76 100755 --- a/maintenance/migration/mongo/import.sh +++ b/maintenance/migration/mongo/import.sh @@ -9,6 +9,8 @@ echo "MONGODB_DATABASE ${MONGODB_DATABASE}" echo "MONGODB_AUTH_DB ${MONGODB_AUTH_DB}" echo "-------------------------------------------------" +create_private_ssh_key_from_env + rm -rf /mongo-export/* ssh -4 -M -S my-ctrl-socket -fnNT -L 27018:localhost:27017 -l ${SSH_USERNAME} ${SSH_HOST} From c50394630c7b796600b8e589d3f1e3e25003456f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Sat, 20 Apr 2019 12:16:40 +0200 Subject: [PATCH 11/24] Simplify maintenance-worker once again After learning how to restore a neo4j database in kubernetes I believe the best way to seed is to seed locally, create a backup and then restore the backup to the kubernetes cluster. --- docker-compose.maintenance.yml | 2 -- maintenance/Dockerfile | 19 ++----------------- maintenance/binaries/import_legacy_uploads | 2 +- maintenance/binaries/reset_db | 4 ---- maintenance/binaries/seed_db | 4 ---- maintenance/known_hosts | 3 +++ maintenance/migration/mongo/import.sh | 7 ++++--- maintenance/migration/neo4j/badges.cql | 2 +- maintenance/migration/neo4j/categories.cql | 2 +- maintenance/migration/neo4j/comments.cql | 2 +- maintenance/migration/neo4j/contributions.cql | 2 +- maintenance/migration/neo4j/follows.cql | 2 +- maintenance/migration/neo4j/shouts.cql | 2 +- maintenance/migration/neo4j/users.cql | 2 +- maintenance/seeding/.env.template | 12 ------------ maintenance/seeding/package.json | 12 ------------ 16 files changed, 17 insertions(+), 62 deletions(-) delete mode 100755 maintenance/binaries/reset_db delete mode 100755 maintenance/binaries/seed_db create mode 100644 maintenance/known_hosts delete mode 100644 maintenance/seeding/.env.template delete mode 100644 maintenance/seeding/package.json diff --git a/docker-compose.maintenance.yml b/docker-compose.maintenance.yml index b18f97513..33a9d28f6 100644 --- a/docker-compose.maintenance.yml +++ b/docker-compose.maintenance.yml @@ -6,7 +6,6 @@ services: build: context: maintenance volumes: - - mongo-export:/mongo-export - uploads:/uploads - neo4j-data:/data - ./maintenance/migration/:/migration @@ -36,7 +35,6 @@ services: - 7474:7474 volumes: - mongo-export: uploads: neo4j-data: diff --git a/maintenance/Dockerfile b/maintenance/Dockerfile index 83154d836..1fafce5e8 100644 --- a/maintenance/Dockerfile +++ b/maintenance/Dockerfile @@ -1,26 +1,11 @@ -FROM humanconnection/neo4j:latest as base +FROM humanconnection/neo4j:latest ENV NODE_ENV=maintenance EXPOSE 7687 7474 -VOLUME /mongo-export /uploads /data RUN apk upgrade --update RUN apk add --no-cache mongodb-tools openssh nodejs yarn rsync -FROM humanconnection/nitro-backend:latest as backend - -FROM base - -COPY --from=backend /nitro-backend /nitro-backend -COPY seeding/package.json . -# Install graphql-request manually, it's required for seeding and not included -# in backend's preinstalled node_modules/ -RUN yarn install && \ - cp -r ./node_modules/* /nitro-backend/node_modules/ && \ - rm -rf ./node_modules/ yarn.lock && \ - mv package.json /nitro-backend/package.json -# We have to do this odd copying to prevent cleaning `node_modules` folder which -# would happen if we `yarn install|add` in the target directory - +COPY known_hosts /root/.ssh/known_hosts COPY migration ./migration COPY ./binaries/* /usr/local/bin/ diff --git a/maintenance/binaries/import_legacy_uploads b/maintenance/binaries/import_legacy_uploads index 73240715b..24ae0fca5 100755 --- a/maintenance/binaries/import_legacy_uploads +++ b/maintenance/binaries/import_legacy_uploads @@ -9,5 +9,5 @@ do fi done -create_private_ssh_key_from_env +[ -z "$SSH_PRIVATE_KEY" ] || create_private_ssh_key_from_env rsync --archive --update --verbose ${SSH_USERNAME}@${SSH_HOST}:${UPLOADS_DIRECTORY}/* /uploads/ diff --git a/maintenance/binaries/reset_db b/maintenance/binaries/reset_db deleted file mode 100755 index ce768089a..000000000 --- a/maintenance/binaries/reset_db +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash -set -e - -cd /nitro-backend && yarn run db:reset diff --git a/maintenance/binaries/seed_db b/maintenance/binaries/seed_db deleted file mode 100755 index ce768089a..000000000 --- a/maintenance/binaries/seed_db +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash -set -e - -cd /nitro-backend && yarn run db:reset diff --git a/maintenance/known_hosts b/maintenance/known_hosts new file mode 100644 index 000000000..947840cb2 --- /dev/null +++ b/maintenance/known_hosts @@ -0,0 +1,3 @@ +|1|GuOYlVEhTowidPs18zj9p5F2j3o=|sDHJYLz9Ftv11oXeGEjs7SpVyg0= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBM5N29bI5CeKu1/RBPyM2fwyf7fuajOO+tyhKe1+CC2sZ1XNB5Ff6t6MtCLNRv2mUuvzTbW/HkisDiA5tuXUHOk= +|1|2KP9NV+Q5g2MrtjAeFSVcs8YeOI=|nf3h4wWVwC4xbBS1kzgzE2tBldk= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNhRK6BeIEUxXlS0z/pOfkUkSPfn33g4J1U3L+MyUQYHm+7agT08799ANJhnvELKE1tt4Vx80I9UR81oxzZcy3E= +|1|HonYIRNhKyroUHPKU1HSZw0+Qzs=|5T1btfwFBz2vNSldhqAIfTbfIgQ= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNhRK6BeIEUxXlS0z/pOfkUkSPfn33g4J1U3L+MyUQYHm+7agT08799ANJhnvELKE1tt4Vx80I9UR81oxzZcy3E= diff --git a/maintenance/migration/mongo/import.sh b/maintenance/migration/mongo/import.sh index 89c255a76..328560bfc 100755 --- a/maintenance/migration/mongo/import.sh +++ b/maintenance/migration/mongo/import.sh @@ -9,15 +9,16 @@ echo "MONGODB_DATABASE ${MONGODB_DATABASE}" echo "MONGODB_AUTH_DB ${MONGODB_AUTH_DB}" echo "-------------------------------------------------" -create_private_ssh_key_from_env +[ -z "$SSH_PRIVATE_KEY" ] || create_private_ssh_key_from_env -rm -rf /mongo-export/* +rm -rf /tmp/mongo-export/* +mkdir -p /tmp/mongo-export ssh -4 -M -S my-ctrl-socket -fnNT -L 27018:localhost:27017 -l ${SSH_USERNAME} ${SSH_HOST} for collection in "categories" "badges" "users" "contributions" "comments" "follows" "shouts" do - mongoexport --host localhost -d ${MONGODB_DATABASE} --port 27018 --username ${MONGODB_USERNAME} --password ${MONGODB_PASSWORD} --authenticationDatabase ${MONGODB_AUTH_DB} --db ${MONGODB_DATABASE} --collection $collection --out "/mongo-export/$collection.json" + mongoexport --host localhost -d ${MONGODB_DATABASE} --port 27018 --username ${MONGODB_USERNAME} --password ${MONGODB_PASSWORD} --authenticationDatabase ${MONGODB_AUTH_DB} --db ${MONGODB_DATABASE} --collection $collection --out "/tmp/mongo-export/$collection.json" done ssh -S my-ctrl-socket -O check -l ${SSH_USERNAME} ${SSH_HOST} diff --git a/maintenance/migration/neo4j/badges.cql b/maintenance/migration/neo4j/badges.cql index 90e4755b4..f4bf67dda 100644 --- a/maintenance/migration/neo4j/badges.cql +++ b/maintenance/migration/neo4j/badges.cql @@ -1,4 +1,4 @@ -CALL apoc.load.json('file:/mongo-export/badges.json') YIELD value as badge +CALL apoc.load.json('file:/tmp/mongo-export/badges.json') YIELD value as badge MERGE(b:Badge {id: badge._id["$oid"]}) ON CREATE SET b.key = badge.key, diff --git a/maintenance/migration/neo4j/categories.cql b/maintenance/migration/neo4j/categories.cql index a2bf6a352..c22354cbe 100644 --- a/maintenance/migration/neo4j/categories.cql +++ b/maintenance/migration/neo4j/categories.cql @@ -1,4 +1,4 @@ -CALL apoc.load.json('file:/mongo-export/categories.json') YIELD value as category +CALL apoc.load.json('file:/tmp/mongo-export/categories.json') YIELD value as category MERGE(c:Category {id: category._id["$oid"]}) ON CREATE SET c.name = category.title, diff --git a/maintenance/migration/neo4j/comments.cql b/maintenance/migration/neo4j/comments.cql index 6709acbc8..eb645108a 100644 --- a/maintenance/migration/neo4j/comments.cql +++ b/maintenance/migration/neo4j/comments.cql @@ -1,4 +1,4 @@ -CALL apoc.load.json('file:/mongo-export/comments.json') YIELD value as json +CALL apoc.load.json('file:/tmp/mongo-export/comments.json') YIELD value as json MERGE (comment:Comment {id: json._id["$oid"]}) ON CREATE SET comment.content = json.content, diff --git a/maintenance/migration/neo4j/contributions.cql b/maintenance/migration/neo4j/contributions.cql index 0c7b18959..134c276cf 100644 --- a/maintenance/migration/neo4j/contributions.cql +++ b/maintenance/migration/neo4j/contributions.cql @@ -1,4 +1,4 @@ -CALL apoc.load.json('file:/mongo-export/contributions.json') YIELD value as post +CALL apoc.load.json('file:/tmp/mongo-export/contributions.json') YIELD value as post MERGE (p:Post {id: post._id["$oid"]}) ON CREATE SET p.title = post.title, diff --git a/maintenance/migration/neo4j/follows.cql b/maintenance/migration/neo4j/follows.cql index 0dad6a435..6f5416723 100644 --- a/maintenance/migration/neo4j/follows.cql +++ b/maintenance/migration/neo4j/follows.cql @@ -1,4 +1,4 @@ -CALL apoc.load.json('file:/mongo-export/follows.json') YIELD value as follow +CALL apoc.load.json('file:/tmp/mongo-export/follows.json') YIELD value as follow MATCH (u1:User {id: follow.userId}), (u2:User {id: follow.foreignId}) MERGE (u1)-[:FOLLOWS]->(u2) ; diff --git a/maintenance/migration/neo4j/shouts.cql b/maintenance/migration/neo4j/shouts.cql index 60aca50c9..cd72ab66b 100644 --- a/maintenance/migration/neo4j/shouts.cql +++ b/maintenance/migration/neo4j/shouts.cql @@ -1,4 +1,4 @@ -CALL apoc.load.json('file:/mongo-export/shouts.json') YIELD value as shout +CALL apoc.load.json('file:/tmp/mongo-export/shouts.json') YIELD value as shout MATCH (u:User {id: shout.userId}), (p:Post {id: shout.foreignId}) MERGE (u)-[:SHOUTED]->(p) ; diff --git a/maintenance/migration/neo4j/users.cql b/maintenance/migration/neo4j/users.cql index 5f87bb273..22eb46882 100644 --- a/maintenance/migration/neo4j/users.cql +++ b/maintenance/migration/neo4j/users.cql @@ -1,4 +1,4 @@ -CALL apoc.load.json('file:/mongo-export/users.json') YIELD value as user +CALL apoc.load.json('file:/tmp/mongo-export/users.json') YIELD value as user MERGE(u:User {id: user._id["$oid"]}) ON CREATE SET u.name = user.name, diff --git a/maintenance/seeding/.env.template b/maintenance/seeding/.env.template deleted file mode 100644 index abc62b2dc..000000000 --- a/maintenance/seeding/.env.template +++ /dev/null @@ -1,12 +0,0 @@ -NEO4J_URI=bolt://localhost:7687 -NEO4J_USER=neo4j -NEO4J_PASSWORD=letmein -GRAPHQL_PORT=4000 -GRAPHQL_URI=http://localhost:4000 -CLIENT_URI=http://localhost:3000 -MOCK=false - -JWT_SECRET="b/&&7b78BF&fv/Vd" -MAPBOX_TOKEN="pk.eyJ1IjoiaHVtYW4tY29ubmVjdGlvbiIsImEiOiJjajl0cnBubGoweTVlM3VwZ2lzNTNud3ZtIn0.KZ8KK9l70omjXbEkkbHGsQ" - -PRIVATE_KEY_PASSPHRASE="a7dsf78sadg87ad87sfagsadg78" diff --git a/maintenance/seeding/package.json b/maintenance/seeding/package.json deleted file mode 100644 index f3e5c115f..000000000 --- a/maintenance/seeding/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "license": "MIT", - "scripts": { - "start": "node dist/", - "db:script:seed": "wait-on tcp:4001 && node dist/seed/seed-db.js", - "db:reset": "node dist/seed/reset-db.js", - "db:seed": "cross-env GRAPHQL_URI=http://localhost:4001 GRAPHQL_PORT=4001 DISABLED_MIDDLEWARES=permissions run-p --race start db:script:seed" - }, - "dependencies": { - "graphql-request": "*" - } -} From 543b7c688855e25c782cd56edda0dbcdd508afeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Sat, 20 Apr 2019 12:28:16 +0200 Subject: [PATCH 12/24] Configure neo4j deployment to have 1 pod max --- deployment/human-connection/deployment-neo4j.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/deployment/human-connection/deployment-neo4j.yaml b/deployment/human-connection/deployment-neo4j.yaml index 2c76a3322..5b89b3656 100644 --- a/deployment/human-connection/deployment-neo4j.yaml +++ b/deployment/human-connection/deployment-neo4j.yaml @@ -6,6 +6,10 @@ namespace: human-connection spec: replicas: 1 + strategy: + rollingUpdate: + maxSurge: 0 + maxUnavailable: "100%" selector: matchLabels: human-connection.org/selector: deployment-human-connection-neo4j From 8b83a27da124829cd26ec7563915a6bccb16b797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Tue, 23 Apr 2019 23:56:35 +0200 Subject: [PATCH 13/24] Structure DO and minikube into separate pages --- SUMMARY.md | 2 + deployment/README.md | 75 +----------------- deployment/digital-ocean/README.md | 75 ++++++++++++++++++ .../digital-ocean/dashboard-screenshot.png | Bin 0 -> 181783 bytes deployment/minikube/README.md | 20 +++++ 5 files changed, 99 insertions(+), 73 deletions(-) create mode 100644 deployment/digital-ocean/README.md create mode 100644 deployment/digital-ocean/dashboard-screenshot.png create mode 100644 deployment/minikube/README.md diff --git a/SUMMARY.md b/SUMMARY.md index e1cf09126..dbd9e4299 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -21,6 +21,8 @@ * [Backend tests](backend/testing.md) * [Contributing](CONTRIBUTING.md) * [Kubernetes Deployment](deployment/README.md) + * [Minikube](deployment/minikube/README.md) + * [Digital Ocean](deployment/digital-ocean/README.md) * [Neo4J DB Backup](deployment/backup.md) * [Maintenance](maintenance/README.md) * [Feature Specification](cypress/features.md) diff --git a/deployment/README.md b/deployment/README.md index 84912d2a5..a288cdce7 100644 --- a/deployment/README.md +++ b/deployment/README.md @@ -4,80 +4,9 @@ We deploy with [kubernetes](https://kubernetes.io/). In order to deploy your own network you have to [install kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) and get a kubernetes cluster. -We have tested two different kubernetes providers: [Minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/) -and [Digital Ocean](https://www.digitalocean.com/). +We have tested two different kubernetes providers: [Minikube](./minikube/README.md) +and [Digital Ocean](./digital-ocean/README.md). -## Minikube - -There are many Kubernetes providers, but if you're just getting started, Minikube is a tool that you can use to get your feet wet. - -[Install Minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/) - -Open minikube dashboard: - -```text -$ minikube dashboard -``` - -This will give you an overview. Some of the steps below need some timing to make ressources available to other dependent deployments. Keeping an eye on the dashboard is a great way to check that. - -Follow the [installation instruction](deployment.md#installation-with-kubernetes) below. If all the pods and services have settled and everything looks green in your minikube dashboard, expose the `nitro-web` service on your host system with: - -```text -$ minikube service nitro-web --namespace=human-connection -``` - -## Digital Ocean - -1. At first, create a cluster on Digital Ocean. -2. Download the config.yaml if the process has finished. -3. Put the config file where you can find it later \(preferable in your home directory under `~/.kube/`\) -4. In the open terminal you can set the current config for the active session: `export KUBECONFIG=~/.kube/THE-NAME-OF-YOUR-CLUSTER-kubeconfig.yaml`. You could make this change permanent by adding the line to your `.bashrc` or `~/.config/fish/config.fish` depending on your shell. - - Otherwise you would have to always add `--kubeconfig ~/.kube/THE-NAME-OF-YOUR-CLUSTER-kubeconfig.yaml` on every `kubectl` command that you are running. - -5. Now check if you can connect to the cluster and if its your newly created one by running: `kubectl get nodes` - -If you got the steps right above and see your nodes you can continue. - -First, install kubernetes dashboard: - -```bash -$ kubectl apply -f dashboard/ -$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/aio/deploy/recommended/kubernetes-dashboard.yaml -``` - -Get your token on the command line: - -```bash -$ kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}') -``` - -It should print something like: - -```text -Name: admin-user-token-6gl6l -Namespace: kube-system -Labels: -Annotations: kubernetes.io/service-account.name=admin-user - kubernetes.io/service-account.uid=b16afba9-dfec-11e7-bbb9-901b0e532516 - -Type: kubernetes.io/service-account-token - -Data -==== -ca.crt: 1025 bytes -namespace: 11 bytes -token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLTZnbDZsIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJiMTZhZmJhOS1kZmVjLTExZTctYmJiOS05MDFiMGU1MzI1MTYiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06YWRtaW4tdXNlciJ9.M70CU3lbu3PP4OjhFms8PVL5pQKj-jj4RNSLA4YmQfTXpPUuxqXjiTf094_Rzr0fgN_IVX6gC4fiNUL5ynx9KU-lkPfk0HnX8scxfJNzypL039mpGt0bbe1IXKSIRaq_9VW59Xz-yBUhycYcKPO9RM2Qa1Ax29nqNVko4vLn1_1wPqJ6XSq3GYI8anTzV8Fku4jasUwjrws6Cn6_sPEGmL54sq5R4Z5afUtv-mItTmqZZdxnkRqcJLlg2Y8WbCPogErbsaCDJoABQ7ppaqHetwfM_0yMun6ABOQbIwwl8pspJhpplKwyo700OSpvTT9zlBsu-b35lzXGBRHzv5g_RA -``` - -Proxy localhost to the remote kubernetes dashboard: - -```bash -$ kubectl proxy -``` - -Grab the token from above and paste it into the login screen at [http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/](http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/) ## Installation with kubernetes diff --git a/deployment/digital-ocean/README.md b/deployment/digital-ocean/README.md new file mode 100644 index 000000000..5431d6338 --- /dev/null +++ b/deployment/digital-ocean/README.md @@ -0,0 +1,75 @@ +# Digital Ocean + +As a start, read the [introduction into kubernetes](https://www.digitalocean.com/community/tutorials/an-introduction-to-kubernetes) by the folks at Digital Ocean. The following section should enable you to deploy Human Connection to your kubernetes cluster. + +## Connect to your local cluster + +1. Create a cluster at [Digital Ocean](https://www.digitalocean.com/). +2. Download the `***-kubeconfig.yaml` from the Web UI. +3. Move the file to the default location where kubectl expects it to be: `mv ***-kubeconfig.yaml ~/.kube/config`. Alternatively you can set the config on every command: `--kubeconfig ***-kubeconfig.yaml` +4. Now check if you can connect to the cluster and if its your newly created one by running: `kubectl get nodes` + +The output should look about like this: +``` +$ kubectl get nodes +NAME STATUS ROLES AGE VERSION +nifty-driscoll-uu1w Ready 69d v1.13.2 +nifty-driscoll-uuiw Ready 69d v1.13.2 +nifty-driscoll-uusn Ready 69d v1.13.2 +``` + +If you got the steps right above and see your nodes you can continue. + +## Install kubernetes dashboard + +The kubernetes dashboard is optional but very helpful for debugging. If you want to install it, you have to do so only **once** per cluster: + +```bash +$ kubectl apply -f dashboard/ +$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/aio/deploy/recommended/kubernetes-dashboard.yaml +``` + +### Login to your dashboard + +Proxy the remote kubernetes dashboard to localhost: + +```bash +$ kubectl proxy +``` + +Visit: + +[http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/](http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/) + +You should see a login screen. + +To get your token for the dashboard you can run this command: + +```bash +$ kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}') +``` + +It should print something like: + +```text +Name: admin-user-token-6gl6l +Namespace: kube-system +Labels: +Annotations: kubernetes.io/service-account.name=admin-user + kubernetes.io/service-account.uid=b16afba9-dfec-11e7-bbb9-901b0e532516 + +Type: kubernetes.io/service-account-token + +Data +==== +ca.crt: 1025 bytes +namespace: 11 bytes +token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLTZnbDZsIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJiMTZhZmJhOS1kZmVjLTExZTctYmJiOS05MDFiMGU1MzI1MTYiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06YWRtaW4tdXNlciJ9.M70CU3lbu3PP4OjhFms8PVL5pQKj-jj4RNSLA4YmQfTXpPUuxqXjiTf094_Rzr0fgN_IVX6gC4fiNUL5ynx9KU-lkPfk0HnX8scxfJNzypL039mpGt0bbe1IXKSIRaq_9VW59Xz-yBUhycYcKPO9RM2Qa1Ax29nqNVko4vLn1_1wPqJ6XSq3GYI8anTzV8Fku4jasUwjrws6Cn6_sPEGmL54sq5R4Z5afUtv-mItTmqZZdxnkRqcJLlg2Y8WbCPogErbsaCDJoABQ7ppaqHetwfM_0yMun6ABOQbIwwl8pspJhpplKwyo700OSpvTT9zlBsu-b35lzXGBRHzv5g_RA +``` + +Grab the token from above and paste it into the [login screen](http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/) + +When you are logged in, you should see sth. like: +![Dashboard](./dashboard-screenshot.png) + +Feel free to save the login token from above in your password manager. Unlike the `kubeconfig` file, this token does not expire. diff --git a/deployment/digital-ocean/dashboard-screenshot.png b/deployment/digital-ocean/dashboard-screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..6aefb5414d26ac3cb69153c0624456e47e4ba88f GIT binary patch literal 181783 zcmeFZX*|?@`!`-n3nyAkibRVRLMas4Qd*QH#@JP2GJ~;%j4gypqOz5>1(`83wz1Dl z2xTuB%P^E>ForPJvHU+%=k>eJ&g;DDe?Pb%-M=UE%E9ULL-GqbFqX@O?@y|8Ad-Tq*N|4@@4DHoYXJlhQp#0_bqx4AmB= zqfC;!4P#1krTW8JFUzCrTNDxP0|81CJ4u31dcr|QgR%~h+{ zZu-@Syk>J<#BZOt;mosDTBvBJ?JZ}2_rSFq(8cS2`AY7B0uOGL9E!*lur2w%m7GIS zPo7*yQnPZLXa!aKS7CL2e$Bn@fp_n&l`%?xD7AmTPJLqhh95_eb5OHcEWEVw`S*>! z)Vn(Iwd!t}VIlI;_Mi5Bu!x7x2MfiTxQM*3{js=vJgbA~2iy0h-}`=?Z+ik(1bF^_ zu$!e^zLl>(_t`i8k3V0yAG{E$+KB$~!Yg-Dm8A_ zc#jWV=&bC6s?|wYW^SY9+l46-U|lLohpwIFI~Tam=*;)sN{aAqK)Y{_)BX8r8*W?x zZ_#xu+wgr$fjhU}yO-)^f$0cqQR21oV>yX>mAI?YX#dPPUb&&LL!XY7yi? z_!;xTyFVV^xN#GEFi{G2`e)gCzXJ$DqHDkO52d;Rm23*4@1Qv6gz4aklsHrm!;;`& z)A`l%HfJu3IWylCECVU9P&v@CQ89dMs`%9EAJ=jd0uGd#WJ~|?m_M~tvmAUaTjW#s zL+V~?OX>$%6X9bn;|fM+$Cn?5Tb(5Y(M4*;i(e|URWL(-lmI&;3�f35BeD2-34X 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 z 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 L0K-54 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 LT8ahoPXhi67M*0s literal 0 HcmV?d00001 diff --git a/deployment/minikube/README.md b/deployment/minikube/README.md new file mode 100644 index 000000000..f1c56d216 --- /dev/null +++ b/deployment/minikube/README.md @@ -0,0 +1,20 @@ +# Minikube + +There are many Kubernetes providers, but if you're just getting started, Minikube is a tool that you can use to get your feet wet. + +[Install Minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/) + +Open minikube dashboard: + +```text +$ minikube dashboard +``` + +This will give you an overview. Some of the steps below need some timing to make ressources available to other dependent deployments. Keeping an eye on the dashboard is a great way to check that. + +Follow the [installation instruction](deployment.md#installation-with-kubernetes) below. If all the pods and services have settled and everything looks green in your minikube dashboard, expose the `nitro-web` service on your host system with: + +```text +$ minikube service nitro-web --namespace=human-connection +``` + From f1c28ee0ccc9f25360afc8488dfe8e7119dbf0ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Wed, 24 Apr 2019 00:15:08 +0200 Subject: [PATCH 14/24] Further structuring in https + dashboard chapters --- SUMMARY.md | 2 + deployment/README.md | 54 ----------------- deployment/digital-ocean/README.md | 57 ++---------------- deployment/digital-ocean/dashboard/README.md | 54 +++++++++++++++++ .../dashboard/admin-user.yaml | 0 .../{ => dashboard}/dashboard-screenshot.png | Bin .../dashboard/role-binding.yaml | 0 deployment/digital-ocean/https/README.md | 57 ++++++++++++++++++ .../https}/ingress.yaml | 0 .../https/issuer.yaml | 0 10 files changed, 117 insertions(+), 107 deletions(-) create mode 100644 deployment/digital-ocean/dashboard/README.md rename deployment/{ => digital-ocean}/dashboard/admin-user.yaml (100%) rename deployment/digital-ocean/{ => dashboard}/dashboard-screenshot.png (100%) rename deployment/{ => digital-ocean}/dashboard/role-binding.yaml (100%) create mode 100644 deployment/digital-ocean/https/README.md rename deployment/{human-connection/ingress => digital-ocean/https}/ingress.yaml (100%) rename deployment/{human-connection => digital-ocean}/https/issuer.yaml (100%) diff --git a/SUMMARY.md b/SUMMARY.md index dbd9e4299..937b2b06b 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -23,6 +23,8 @@ * [Kubernetes Deployment](deployment/README.md) * [Minikube](deployment/minikube/README.md) * [Digital Ocean](deployment/digital-ocean/README.md) + * [Kubernetes Dashboard](deployment/digital-ocean/dashboard/README.md) + * [HTTPS](deployment/digital-ocean/https/README.md) * [Neo4J DB Backup](deployment/backup.md) * [Maintenance](maintenance/README.md) * [Feature Specification](cypress/features.md) diff --git a/deployment/README.md b/deployment/README.md index a288cdce7..77e03e722 100644 --- a/deployment/README.md +++ b/deployment/README.md @@ -46,60 +46,6 @@ $ kubectl apply -f human-connection/ This can take a while because kubernetes will download the docker images. Sit back and relax and have a look into your kubernetes dashboard. Wait until all pods turn green and they don't show a warning `Waiting: ContainerCreating` anymore. -#### Setup Ingress and HTTPS - -Follow [this quick start guide](https://docs.cert-manager.io/en/latest/tutorials/acme/quick-start/index.html) and install certmanager via helm and tiller: - -```text -$ kubectl create serviceaccount tiller --namespace=kube-system -$ kubectl create clusterrolebinding tiller-admin --serviceaccount=kube-system:tiller --clusterrole=cluster-admin -$ helm init --service-account=tiller -$ helm repo update -$ helm install stable/nginx-ingress -$ kubectl apply -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.6/deploy/manifests/00-crds.yaml -$ helm install --name cert-manager --namespace cert-manager stable/cert-manager -``` - -Create letsencrypt issuers. _Change the email address_ in these files before running this command. - -```bash -$ kubectl apply -f human-connection/https/ -``` - -Create an ingress service in namespace `human-connection`. _Change the domain name_ according to your needs: - -```bash -$ kubectl apply -f human-connection/ingress/ -``` - -Check the ingress server is working correctly: - -```bash -$ curl -kivL -H 'Host: ' 'https://' -``` - -If the response looks good, configure your domain registrar for the new IP address and the domain. - -Now let's get a valid HTTPS certificate. According to the tutorial above, check your tls certificate for staging: - -```bash -$ kubectl describe --namespace=human-connection certificate tls -$ kubectl describe --namespace=human-connection secret tls -``` - -If everything looks good, update the issuer of your ingress. Change the annotation `certmanager.k8s.io/issuer` from `letsencrypt-staging` to `letsencrypt-prod` in your ingress configuration in `human-connection/ingress/ingress.yaml`. - -```bash -$ kubectl apply -f human-connection/ingress/ingress.yaml -``` - -Delete the former secret to force a refresh: - -```text -$ kubectl --namespace=human-connection delete secret tls -``` - -Now, HTTPS should be configured on your domain. Congrats. #### Legacy data migration diff --git a/deployment/digital-ocean/README.md b/deployment/digital-ocean/README.md index 5431d6338..12c272691 100644 --- a/deployment/digital-ocean/README.md +++ b/deployment/digital-ocean/README.md @@ -20,56 +20,7 @@ nifty-driscoll-uusn Ready 69d v1.13.2 If you got the steps right above and see your nodes you can continue. -## Install kubernetes dashboard - -The kubernetes dashboard is optional but very helpful for debugging. If you want to install it, you have to do so only **once** per cluster: - -```bash -$ kubectl apply -f dashboard/ -$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/aio/deploy/recommended/kubernetes-dashboard.yaml -``` - -### Login to your dashboard - -Proxy the remote kubernetes dashboard to localhost: - -```bash -$ kubectl proxy -``` - -Visit: - -[http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/](http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/) - -You should see a login screen. - -To get your token for the dashboard you can run this command: - -```bash -$ kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}') -``` - -It should print something like: - -```text -Name: admin-user-token-6gl6l -Namespace: kube-system -Labels: -Annotations: kubernetes.io/service-account.name=admin-user - kubernetes.io/service-account.uid=b16afba9-dfec-11e7-bbb9-901b0e532516 - -Type: kubernetes.io/service-account-token - -Data -==== -ca.crt: 1025 bytes -namespace: 11 bytes -token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLTZnbDZsIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJiMTZhZmJhOS1kZmVjLTExZTctYmJiOS05MDFiMGU1MzI1MTYiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06YWRtaW4tdXNlciJ9.M70CU3lbu3PP4OjhFms8PVL5pQKj-jj4RNSLA4YmQfTXpPUuxqXjiTf094_Rzr0fgN_IVX6gC4fiNUL5ynx9KU-lkPfk0HnX8scxfJNzypL039mpGt0bbe1IXKSIRaq_9VW59Xz-yBUhycYcKPO9RM2Qa1Ax29nqNVko4vLn1_1wPqJ6XSq3GYI8anTzV8Fku4jasUwjrws6Cn6_sPEGmL54sq5R4Z5afUtv-mItTmqZZdxnkRqcJLlg2Y8WbCPogErbsaCDJoABQ7ppaqHetwfM_0yMun6ABOQbIwwl8pspJhpplKwyo700OSpvTT9zlBsu-b35lzXGBRHzv5g_RA -``` - -Grab the token from above and paste it into the [login screen](http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/) - -When you are logged in, you should see sth. like: -![Dashboard](./dashboard-screenshot.png) - -Feel free to save the login token from above in your password manager. Unlike the `kubeconfig` file, this token does not expire. +Digital Ocean kubernetes clusters don't have a graphical interface, so I suggest +to setup the [kubernetes dashboard](./dashboard/README.md) as a next step. +Configuring [HTTPS](./https/README.md) is bit tricky and therefore I suggest to +do this as a last step. diff --git a/deployment/digital-ocean/dashboard/README.md b/deployment/digital-ocean/dashboard/README.md new file mode 100644 index 000000000..a2e5446b2 --- /dev/null +++ b/deployment/digital-ocean/dashboard/README.md @@ -0,0 +1,54 @@ +# Install Kubernetes Dashboard + +The kubernetes dashboard is optional but very helpful for debugging. If you want to install it, you have to do so only **once** per cluster: + +```bash +# in folder deployment/digital-ocean/ +$ kubectl apply -f dashboard/ +$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/aio/deploy/recommended/kubernetes-dashboard.yaml +``` + +### Login to your dashboard + +Proxy the remote kubernetes dashboard to localhost: + +```bash +$ kubectl proxy +``` + +Visit: + +[http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/](http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/) + +You should see a login screen. + +To get your token for the dashboard you can run this command: + +```bash +$ kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}') +``` + +It should print something like: + +```text +Name: admin-user-token-6gl6l +Namespace: kube-system +Labels: +Annotations: kubernetes.io/service-account.name=admin-user + kubernetes.io/service-account.uid=b16afba9-dfec-11e7-bbb9-901b0e532516 + +Type: kubernetes.io/service-account-token + +Data +==== +ca.crt: 1025 bytes +namespace: 11 bytes +token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLTZnbDZsIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJiMTZhZmJhOS1kZmVjLTExZTctYmJiOS05MDFiMGU1MzI1MTYiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06YWRtaW4tdXNlciJ9.M70CU3lbu3PP4OjhFms8PVL5pQKj-jj4RNSLA4YmQfTXpPUuxqXjiTf094_Rzr0fgN_IVX6gC4fiNUL5ynx9KU-lkPfk0HnX8scxfJNzypL039mpGt0bbe1IXKSIRaq_9VW59Xz-yBUhycYcKPO9RM2Qa1Ax29nqNVko4vLn1_1wPqJ6XSq3GYI8anTzV8Fku4jasUwjrws6Cn6_sPEGmL54sq5R4Z5afUtv-mItTmqZZdxnkRqcJLlg2Y8WbCPogErbsaCDJoABQ7ppaqHetwfM_0yMun6ABOQbIwwl8pspJhpplKwyo700OSpvTT9zlBsu-b35lzXGBRHzv5g_RA +``` + +Grab the token from above and paste it into the [login screen](http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/) + +When you are logged in, you should see sth. like: +![Dashboard](./dashboard-screenshot.png) + +Feel free to save the login token from above in your password manager. Unlike the `kubeconfig` file, this token does not expire. diff --git a/deployment/dashboard/admin-user.yaml b/deployment/digital-ocean/dashboard/admin-user.yaml similarity index 100% rename from deployment/dashboard/admin-user.yaml rename to deployment/digital-ocean/dashboard/admin-user.yaml diff --git a/deployment/digital-ocean/dashboard-screenshot.png b/deployment/digital-ocean/dashboard/dashboard-screenshot.png similarity index 100% rename from deployment/digital-ocean/dashboard-screenshot.png rename to deployment/digital-ocean/dashboard/dashboard-screenshot.png diff --git a/deployment/dashboard/role-binding.yaml b/deployment/digital-ocean/dashboard/role-binding.yaml similarity index 100% rename from deployment/dashboard/role-binding.yaml rename to deployment/digital-ocean/dashboard/role-binding.yaml diff --git a/deployment/digital-ocean/https/README.md b/deployment/digital-ocean/https/README.md new file mode 100644 index 000000000..398601e78 --- /dev/null +++ b/deployment/digital-ocean/https/README.md @@ -0,0 +1,57 @@ +# Setup Ingress and HTTPS + +Follow [this quick start guide](https://docs.cert-manager.io/en/latest/tutorials/acme/quick-start/index.html) and install certmanager via helm and tiller: + +```text +$ kubectl create serviceaccount tiller --namespace=kube-system +$ kubectl create clusterrolebinding tiller-admin --serviceaccount=kube-system:tiller --clusterrole=cluster-admin +$ helm init --service-account=tiller +$ helm repo update +$ helm install stable/nginx-ingress +$ kubectl apply -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.6/deploy/manifests/00-crds.yaml +$ helm install --name cert-manager --namespace cert-manager stable/cert-manager +``` + +Create letsencrypt issuers. _Change the email address_ in these files before running this command. + +```bash +# in folder deployment/digital-ocean/https/ +$ kubectl apply -f issuer.yaml +``` + +Create an ingress service in namespace `human-connection`. _Change the domain name_ according to your needs: + +```bash +# in folder deployment/digital-ocean/https/ +$ kubectl apply -f ingress.yaml +``` + +Check the ingress server is working correctly: + +```bash +$ curl -kivL -H 'Host: ' 'https://' +``` + +If the response looks good, configure your domain registrar for the new IP address and the domain. + +Now let's get a valid HTTPS certificate. According to the tutorial above, check your tls certificate for staging: + +```bash +$ kubectl describe --namespace=human-connection certificate tls +$ kubectl describe --namespace=human-connection secret tls +``` + +If everything looks good, update the issuer of your ingress. Change the annotation `certmanager.k8s.io/issuer` from `letsencrypt-staging` to `letsencrypt-prod` in your ingress configuration in `ingress.yaml`. + +```bash +# in folder deployment/digital-ocean/https/ +$ kubectl apply -f ingress.yaml +``` + +Delete the former secret to force a refresh: + +```text +$ kubectl --namespace=human-connection delete secret tls +``` + +Now, HTTPS should be configured on your domain. Congrats. diff --git a/deployment/human-connection/ingress/ingress.yaml b/deployment/digital-ocean/https/ingress.yaml similarity index 100% rename from deployment/human-connection/ingress/ingress.yaml rename to deployment/digital-ocean/https/ingress.yaml diff --git a/deployment/human-connection/https/issuer.yaml b/deployment/digital-ocean/https/issuer.yaml similarity index 100% rename from deployment/human-connection/https/issuer.yaml rename to deployment/digital-ocean/https/issuer.yaml From 6ed5ad58d5cd216847d32ca7e8e73fd645ce72c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Wed, 24 Apr 2019 00:26:37 +0200 Subject: [PATCH 15/24] Merge maintenance with deployment --- SUMMARY.md | 2 +- deployment/README.md | 54 ------------------ deployment/legacy-migration/README.md | 55 +++++++++++++++++++ .../maintenance-worker}/.dockerignore | 0 .../maintenance-worker}/.gitignore | 0 .../maintenance-worker}/Dockerfile | 0 .../maintenance-worker}/README.md | 0 .../binaries/create_private_ssh_key_from_env | 0 .../binaries/import_legacy_db | 0 .../binaries/import_legacy_uploads | 0 .../maintenance-worker}/known_hosts | 0 .../migration/mongo/import.sh | 0 .../migration/neo4j/badges.cql | 0 .../migration/neo4j/categories.cql | 0 .../migration/neo4j/comments.cql | 0 .../migration/neo4j/contributions.cql | 0 .../migration/neo4j/follows.cql | 0 .../migration/neo4j/import.sh | 0 .../migration/neo4j/shouts.cql | 0 .../migration/neo4j/users.cql | 0 docker-compose.maintenance.yml | 4 +- 21 files changed, 58 insertions(+), 57 deletions(-) create mode 100644 deployment/legacy-migration/README.md rename {maintenance => deployment/legacy-migration/maintenance-worker}/.dockerignore (100%) rename {maintenance => deployment/legacy-migration/maintenance-worker}/.gitignore (100%) rename {maintenance => deployment/legacy-migration/maintenance-worker}/Dockerfile (100%) rename {maintenance => deployment/legacy-migration/maintenance-worker}/README.md (100%) rename {maintenance => deployment/legacy-migration/maintenance-worker}/binaries/create_private_ssh_key_from_env (100%) rename {maintenance => deployment/legacy-migration/maintenance-worker}/binaries/import_legacy_db (100%) rename {maintenance => deployment/legacy-migration/maintenance-worker}/binaries/import_legacy_uploads (100%) rename {maintenance => deployment/legacy-migration/maintenance-worker}/known_hosts (100%) rename {maintenance => deployment/legacy-migration/maintenance-worker}/migration/mongo/import.sh (100%) rename {maintenance => deployment/legacy-migration/maintenance-worker}/migration/neo4j/badges.cql (100%) rename {maintenance => deployment/legacy-migration/maintenance-worker}/migration/neo4j/categories.cql (100%) rename {maintenance => deployment/legacy-migration/maintenance-worker}/migration/neo4j/comments.cql (100%) rename {maintenance => deployment/legacy-migration/maintenance-worker}/migration/neo4j/contributions.cql (100%) rename {maintenance => deployment/legacy-migration/maintenance-worker}/migration/neo4j/follows.cql (100%) rename {maintenance => deployment/legacy-migration/maintenance-worker}/migration/neo4j/import.sh (100%) rename {maintenance => deployment/legacy-migration/maintenance-worker}/migration/neo4j/shouts.cql (100%) rename {maintenance => deployment/legacy-migration/maintenance-worker}/migration/neo4j/users.cql (100%) diff --git a/SUMMARY.md b/SUMMARY.md index 937b2b06b..54a751238 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -26,7 +26,7 @@ * [Kubernetes Dashboard](deployment/digital-ocean/dashboard/README.md) * [HTTPS](deployment/digital-ocean/https/README.md) * [Neo4J DB Backup](deployment/backup.md) -* [Maintenance](maintenance/README.md) + * [Legacy Migration](deployment/legacy-migration/README.md) * [Feature Specification](cypress/features.md) * [Code of conduct](CODE_OF_CONDUCT.md) * [License](LICENSE.md) diff --git a/deployment/README.md b/deployment/README.md index 77e03e722..e191bddc0 100644 --- a/deployment/README.md +++ b/deployment/README.md @@ -45,57 +45,3 @@ $ kubectl apply -f human-connection/ ``` This can take a while because kubernetes will download the docker images. Sit back and relax and have a look into your kubernetes dashboard. Wait until all pods turn green and they don't show a warning `Waiting: ContainerCreating` anymore. - - -#### Legacy data migration - -This setup is completely optional and only required if you have data on a server which is running our legacy code and you want to import that data. It will import the uploads folder and migrate a dump of mongodb into neo4j. - -**Prepare migration of Human Connection legacy server** - -Create a configmap with the specific connection data of your legacy server: - -```bash -$ kubectl create configmap maintenance-worker \ - --namespace=human-connection \ - --from-literal=SSH_USERNAME=someuser \ - --from-literal=SSH_HOST=yourhost \ - --from-literal=MONGODB_USERNAME=hc-api \ - --from-literal=MONGODB_PASSWORD=secretpassword \ - --from-literal=MONGODB_AUTH_DB=hc_api \ - --from-literal=MONGODB_DATABASE=hc_api \ - --from-literal=UPLOADS_DIRECTORY=/var/www/api/uploads \ - --from-literal=NEO4J_URI=bolt://localhost:7687 -``` - -Create a secret with your public and private ssh keys. As the [kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/secret/#use-case-pod-with-ssh-keys) points out, you should be careful with your ssh keys. Anyone with access to your cluster will have access to your ssh keys. Better create a new pair with `ssh-keygen` and copy the public key to your legacy server with `ssh-copy-id`: - -```bash -$ kubectl create secret generic ssh-keys \ - --namespace=human-connection \ - --from-file=id_rsa=/path/to/.ssh/id_rsa \ - --from-file=id_rsa.pub=/path/to/.ssh/id_rsa.pub \ - --from-file=known_hosts=/path/to/.ssh/known_hosts -``` - -**Migrate legacy database** - -Patch the existing deployments to use a multi-container setup: - -```bash -cd legacy-migration -kubectl apply -f volume-claim-mongo-export.yaml -kubectl patch --namespace=human-connection deployment nitro-backend --patch "$(cat deployment-backend.yaml)" -kubectl patch --namespace=human-connection deployment nitro-neo4j --patch "$(cat deployment-neo4j.yaml)" -cd .. -``` - -Run the migration: - -```text -$ kubectl --namespace=human-connection get pods -# change below -$ kubectl --namespace=human-connection exec -it nitro-neo4j-65bbdb597c-nc2lv migrate -$ kubectl --namespace=human-connection exec -it nitro-backend-c6cc5ff69-8h96z sync_uploads -``` - diff --git a/deployment/legacy-migration/README.md b/deployment/legacy-migration/README.md new file mode 100644 index 000000000..419a9a1dc --- /dev/null +++ b/deployment/legacy-migration/README.md @@ -0,0 +1,55 @@ +# Legacy data migration + +This setup is **completely optional** and only required if you have data on a +server which is running our legacy code and you want to import that data. It +will import the uploads folder and migrate a dump of the legacy Mongo database +into our new Neo4J graph database. + +**Prepare migration of Human Connection legacy server** + +Create a configmap with the specific connection data of your legacy server: + +```bash +$ kubectl create configmap maintenance-worker \ + --namespace=human-connection \ + --from-literal=SSH_USERNAME=someuser \ + --from-literal=SSH_HOST=yourhost \ + --from-literal=MONGODB_USERNAME=hc-api \ + --from-literal=MONGODB_PASSWORD=secretpassword \ + --from-literal=MONGODB_AUTH_DB=hc_api \ + --from-literal=MONGODB_DATABASE=hc_api \ + --from-literal=UPLOADS_DIRECTORY=/var/www/api/uploads \ + --from-literal=NEO4J_URI=bolt://localhost:7687 +``` + +Create a secret with your public and private ssh keys. As the [kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/secret/#use-case-pod-with-ssh-keys) points out, you should be careful with your ssh keys. Anyone with access to your cluster will have access to your ssh keys. Better create a new pair with `ssh-keygen` and copy the public key to your legacy server with `ssh-copy-id`: + +```bash +$ kubectl create secret generic ssh-keys \ + --namespace=human-connection \ + --from-file=id_rsa=/path/to/.ssh/id_rsa \ + --from-file=id_rsa.pub=/path/to/.ssh/id_rsa.pub \ + --from-file=known_hosts=/path/to/.ssh/known_hosts +``` + +**Migrate legacy database** + +Patch the existing deployments to use a multi-container setup: + +```bash +cd legacy-migration +kubectl apply -f volume-claim-mongo-export.yaml +kubectl patch --namespace=human-connection deployment nitro-backend --patch "$(cat deployment-backend.yaml)" +kubectl patch --namespace=human-connection deployment nitro-neo4j --patch "$(cat deployment-neo4j.yaml)" +cd .. +``` + +Run the migration: + +```text +$ kubectl --namespace=human-connection get pods +# change below +$ kubectl --namespace=human-connection exec -it nitro-neo4j-65bbdb597c-nc2lv migrate +$ kubectl --namespace=human-connection exec -it nitro-backend-c6cc5ff69-8h96z sync_uploads +``` + diff --git a/maintenance/.dockerignore b/deployment/legacy-migration/maintenance-worker/.dockerignore similarity index 100% rename from maintenance/.dockerignore rename to deployment/legacy-migration/maintenance-worker/.dockerignore diff --git a/maintenance/.gitignore b/deployment/legacy-migration/maintenance-worker/.gitignore similarity index 100% rename from maintenance/.gitignore rename to deployment/legacy-migration/maintenance-worker/.gitignore diff --git a/maintenance/Dockerfile b/deployment/legacy-migration/maintenance-worker/Dockerfile similarity index 100% rename from maintenance/Dockerfile rename to deployment/legacy-migration/maintenance-worker/Dockerfile diff --git a/maintenance/README.md b/deployment/legacy-migration/maintenance-worker/README.md similarity index 100% rename from maintenance/README.md rename to deployment/legacy-migration/maintenance-worker/README.md diff --git a/maintenance/binaries/create_private_ssh_key_from_env b/deployment/legacy-migration/maintenance-worker/binaries/create_private_ssh_key_from_env similarity index 100% rename from maintenance/binaries/create_private_ssh_key_from_env rename to deployment/legacy-migration/maintenance-worker/binaries/create_private_ssh_key_from_env diff --git a/maintenance/binaries/import_legacy_db b/deployment/legacy-migration/maintenance-worker/binaries/import_legacy_db similarity index 100% rename from maintenance/binaries/import_legacy_db rename to deployment/legacy-migration/maintenance-worker/binaries/import_legacy_db diff --git a/maintenance/binaries/import_legacy_uploads b/deployment/legacy-migration/maintenance-worker/binaries/import_legacy_uploads similarity index 100% rename from maintenance/binaries/import_legacy_uploads rename to deployment/legacy-migration/maintenance-worker/binaries/import_legacy_uploads diff --git a/maintenance/known_hosts b/deployment/legacy-migration/maintenance-worker/known_hosts similarity index 100% rename from maintenance/known_hosts rename to deployment/legacy-migration/maintenance-worker/known_hosts diff --git a/maintenance/migration/mongo/import.sh b/deployment/legacy-migration/maintenance-worker/migration/mongo/import.sh similarity index 100% rename from maintenance/migration/mongo/import.sh rename to deployment/legacy-migration/maintenance-worker/migration/mongo/import.sh diff --git a/maintenance/migration/neo4j/badges.cql b/deployment/legacy-migration/maintenance-worker/migration/neo4j/badges.cql similarity index 100% rename from maintenance/migration/neo4j/badges.cql rename to deployment/legacy-migration/maintenance-worker/migration/neo4j/badges.cql diff --git a/maintenance/migration/neo4j/categories.cql b/deployment/legacy-migration/maintenance-worker/migration/neo4j/categories.cql similarity index 100% rename from maintenance/migration/neo4j/categories.cql rename to deployment/legacy-migration/maintenance-worker/migration/neo4j/categories.cql diff --git a/maintenance/migration/neo4j/comments.cql b/deployment/legacy-migration/maintenance-worker/migration/neo4j/comments.cql similarity index 100% rename from maintenance/migration/neo4j/comments.cql rename to deployment/legacy-migration/maintenance-worker/migration/neo4j/comments.cql diff --git a/maintenance/migration/neo4j/contributions.cql b/deployment/legacy-migration/maintenance-worker/migration/neo4j/contributions.cql similarity index 100% rename from maintenance/migration/neo4j/contributions.cql rename to deployment/legacy-migration/maintenance-worker/migration/neo4j/contributions.cql diff --git a/maintenance/migration/neo4j/follows.cql b/deployment/legacy-migration/maintenance-worker/migration/neo4j/follows.cql similarity index 100% rename from maintenance/migration/neo4j/follows.cql rename to deployment/legacy-migration/maintenance-worker/migration/neo4j/follows.cql diff --git a/maintenance/migration/neo4j/import.sh b/deployment/legacy-migration/maintenance-worker/migration/neo4j/import.sh similarity index 100% rename from maintenance/migration/neo4j/import.sh rename to deployment/legacy-migration/maintenance-worker/migration/neo4j/import.sh diff --git a/maintenance/migration/neo4j/shouts.cql b/deployment/legacy-migration/maintenance-worker/migration/neo4j/shouts.cql similarity index 100% rename from maintenance/migration/neo4j/shouts.cql rename to deployment/legacy-migration/maintenance-worker/migration/neo4j/shouts.cql diff --git a/maintenance/migration/neo4j/users.cql b/deployment/legacy-migration/maintenance-worker/migration/neo4j/users.cql similarity index 100% rename from maintenance/migration/neo4j/users.cql rename to deployment/legacy-migration/maintenance-worker/migration/neo4j/users.cql diff --git a/docker-compose.maintenance.yml b/docker-compose.maintenance.yml index 33a9d28f6..551a7e244 100644 --- a/docker-compose.maintenance.yml +++ b/docker-compose.maintenance.yml @@ -4,11 +4,11 @@ services: maintenance: image: humanconnection/maintenance-worker:latest build: - context: maintenance + context: deployment/legacy-migration/maintenance-worker volumes: - uploads:/uploads - neo4j-data:/data - - ./maintenance/migration/:/migration + - ./deployment/legacy-migration/maintenance-worker/migration/:/migration networks: - hc-network environment: From 31cff1020648aa274281892ab9e68c3c6891d228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Wed, 24 Apr 2019 01:10:35 +0200 Subject: [PATCH 16/24] Clean up kubernetes config for maintenance-worker We're going into the direction of removing the backend and database deployments, accessing `/uploads` and `/data` through the maintenance worker pod and carrying out tasks from there. --- deployment/legacy-migration/README.md | 47 +++++++++++++------ .../db-migration-worker.yaml | 19 +------- .../legacy-migration/deployment-backend.yaml | 27 ----------- .../legacy-migration/deployment-neo4j.yaml | 39 --------------- .../maintenance-worker/README.md | 40 ---------------- .../maintenance-worker/docker-compose.yml | 4 +- .../volume-claim-mongo-export.yaml | 12 ----- 7 files changed, 37 insertions(+), 151 deletions(-) rename deployment/{ => legacy-migration}/db-migration-worker.yaml (57%) delete mode 100644 deployment/legacy-migration/deployment-backend.yaml delete mode 100644 deployment/legacy-migration/deployment-neo4j.yaml delete mode 100644 deployment/legacy-migration/maintenance-worker/README.md rename docker-compose.maintenance.yml => deployment/legacy-migration/maintenance-worker/docker-compose.yml (88%) delete mode 100644 deployment/legacy-migration/volume-claim-mongo-export.yaml diff --git a/deployment/legacy-migration/README.md b/deployment/legacy-migration/README.md index 419a9a1dc..eeb613146 100644 --- a/deployment/legacy-migration/README.md +++ b/deployment/legacy-migration/README.md @@ -5,7 +5,7 @@ server which is running our legacy code and you want to import that data. It will import the uploads folder and migrate a dump of the legacy Mongo database into our new Neo4J graph database. -**Prepare migration of Human Connection legacy server** +## Configure Maintenance-Worker Pod Create a configmap with the specific connection data of your legacy server: @@ -19,7 +19,6 @@ $ kubectl create configmap maintenance-worker \ --from-literal=MONGODB_AUTH_DB=hc_api \ --from-literal=MONGODB_DATABASE=hc_api \ --from-literal=UPLOADS_DIRECTORY=/var/www/api/uploads \ - --from-literal=NEO4J_URI=bolt://localhost:7687 ``` Create a secret with your public and private ssh keys. As the [kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/secret/#use-case-pod-with-ssh-keys) points out, you should be careful with your ssh keys. Anyone with access to your cluster will have access to your ssh keys. Better create a new pair with `ssh-keygen` and copy the public key to your legacy server with `ssh-copy-id`: @@ -32,24 +31,44 @@ $ kubectl create secret generic ssh-keys \ --from-file=known_hosts=/path/to/.ssh/known_hosts ``` -**Migrate legacy database** +## Deploy a Temporary Maintenance-Worker Pod -Patch the existing deployments to use a multi-container setup: +Bring the application into maintenance mode. +{% hint style="info" %} TODO: implement maintenance mode {% endhint %} + + +Then temporarily delete backend and database deployments ```bash -cd legacy-migration -kubectl apply -f volume-claim-mongo-export.yaml -kubectl patch --namespace=human-connection deployment nitro-backend --patch "$(cat deployment-backend.yaml)" -kubectl patch --namespace=human-connection deployment nitro-neo4j --patch "$(cat deployment-neo4j.yaml)" -cd .. +$ kubectl --namespace=human-connection get deployments +NAME READY UP-TO-DATE AVAILABLE AGE +nitro-backend 1/1 1 1 3d11h +nitro-neo4j 1/1 1 1 3d11h +nitro-web 2/2 2 2 73d +$ kubectl --namespace=human-connection delete deployment nitro-neo4j +deployment.extensions "nitro-neo4j" deleted +$ kubectl --namespace=human-connection delete deployment nitro-backend +deployment.extensions "nitro-backend" deleted ``` -Run the migration: +Deploy one-time maintenance-worker pod: +``` +# in deployment/legacy-migration/ +$ kubectl apply -f db-migration-worker.yaml +pod/nitro-maintenance-worker created +``` + +Import legacy database and uploads: ```text -$ kubectl --namespace=human-connection get pods -# change below -$ kubectl --namespace=human-connection exec -it nitro-neo4j-65bbdb597c-nc2lv migrate -$ kubectl --namespace=human-connection exec -it nitro-backend-c6cc5ff69-8h96z sync_uploads +$ kubectl --namespace=human-connection exec -it nitro-maintenance-worker bash +$ import_legacy_db +$ import_uploads +$ exit +``` + +Delete the pod when you're done: +``` +$ kubectl --namespace=human-connection delete pod nitro-maintenance-worker ``` diff --git a/deployment/db-migration-worker.yaml b/deployment/legacy-migration/db-migration-worker.yaml similarity index 57% rename from deployment/db-migration-worker.yaml rename to deployment/legacy-migration/db-migration-worker.yaml index 8ee86f11c..f983bd5bb 100644 --- a/deployment/db-migration-worker.yaml +++ b/deployment/legacy-migration/db-migration-worker.yaml @@ -10,30 +10,15 @@ secret: secretName: ssh-keys defaultMode: 0400 - - name: mongo-export - persistentVolumeClaim: - claimName: mongo-export-claim containers: - name: nitro-maintenance-worker image: humanconnection/maintenance-worker:latest envFrom: - configMapRef: name: maintenance-worker + - configMapRef: + name: configmap volumeMounts: - name: secret-volume readOnly: false mountPath: /root/.ssh - - name: mongo-export - mountPath: /mongo-export/ ---- - kind: PersistentVolumeClaim - apiVersion: v1 - metadata: - name: mongo-export-claim - namespace: human-connection - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 1Gi diff --git a/deployment/legacy-migration/deployment-backend.yaml b/deployment/legacy-migration/deployment-backend.yaml deleted file mode 100644 index 1bcb380aa..000000000 --- a/deployment/legacy-migration/deployment-backend.yaml +++ /dev/null @@ -1,27 +0,0 @@ ---- - apiVersion: extensions/v1beta1 - kind: Deployment - metadata: - name: nitro-backend - namespace: human-connection - spec: - template: - spec: - containers: - - name: nitro-maintenance-worker - image: humanconnection/maintenance-worker:latest - imagePullPolicy: Always - envFrom: - - configMapRef: - name: maintenance-worker - volumeMounts: - - name: secret-volume - readOnly: false - mountPath: /root/.ssh - - name: uploads - mountPath: /uploads/ - volumes: - - name: secret-volume - secret: - secretName: ssh-keys - defaultMode: 0400 diff --git a/deployment/legacy-migration/deployment-neo4j.yaml b/deployment/legacy-migration/deployment-neo4j.yaml deleted file mode 100644 index 0f3252984..000000000 --- a/deployment/legacy-migration/deployment-neo4j.yaml +++ /dev/null @@ -1,39 +0,0 @@ ---- - apiVersion: extensions/v1beta1 - kind: Deployment - metadata: - name: nitro-neo4j - namespace: human-connection - spec: - template: - spec: - containers: - - name: nitro-maintenance-worker - image: humanconnection/maintenance-worker:latest - imagePullPolicy: Always - envFrom: - - configMapRef: - name: maintenance-worker - env: - - name: COMMIT - value: - - name: NEO4J_URI - value: bolt://localhost:7687 - volumeMounts: - - name: secret-volume - readOnly: false - mountPath: /root/.ssh - - name: mongo-export - mountPath: /mongo-export/ - - name: nitro-neo4j - volumeMounts: - - mountPath: /mongo-export/ - name: mongo-export - volumes: - - name: secret-volume - secret: - secretName: ssh-keys - defaultMode: 0400 - - name: mongo-export - persistentVolumeClaim: - claimName: mongo-export-claim diff --git a/deployment/legacy-migration/maintenance-worker/README.md b/deployment/legacy-migration/maintenance-worker/README.md deleted file mode 100644 index 4b93686ac..000000000 --- a/deployment/legacy-migration/maintenance-worker/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Legacy Migration - -This guide helps you to import data from our legacy servers, which are using FeathersJS and MongoDB. - -**You can skip this if you don't plan to migrate any legacy applications!** - -## Prerequisites - -You need [docker](https://www.docker.com/) installed on your machine. Furthermore you need SSH access to the server and you need to know the following login credentials and server settings: - -| Environment variable | Description | -| :--- | :--- | -| SSH\_USERNAME | Your ssh username on the server | -| SSH\_HOST | The IP address of the server | -| MONGODB\_USERNAME | Mongo username on the server | -| MONGODB\_PASSWORD | Mongo password on the server | -| MONGODB\_AUTH\_DB | Mongo authentication database | -| MONGODB\_DATABASE | The name of the mongo database | -| UPLOADS\_DIRECTORY | Path to remote uploads folder | - -## Run the database migration - -Run `docker-compose` with all environment variables specified: - -```bash -SSH_USERNAME=username SSH_HOST=some.server.com MONGODB_USERNAME='hc-api' MONGODB_PASSWORD='secret' MONGODB_DATABASE=hc_api MONGODB_AUTH_DB=hc_api UPLOADS_DIRECTORY=/var/www/api/uploads docker-compose up -``` - -Download the remote mongo database: - -```bash -docker-compose exec maintenance-worker ./import.sh -``` - -Import the local download into Neo4J: - -```bash -docker-compose exec neo4j import/import.sh -``` - diff --git a/docker-compose.maintenance.yml b/deployment/legacy-migration/maintenance-worker/docker-compose.yml similarity index 88% rename from docker-compose.maintenance.yml rename to deployment/legacy-migration/maintenance-worker/docker-compose.yml index 551a7e244..a45a5163a 100644 --- a/docker-compose.maintenance.yml +++ b/deployment/legacy-migration/maintenance-worker/docker-compose.yml @@ -4,11 +4,11 @@ services: maintenance: image: humanconnection/maintenance-worker:latest build: - context: deployment/legacy-migration/maintenance-worker + context: . volumes: - uploads:/uploads - neo4j-data:/data - - ./deployment/legacy-migration/maintenance-worker/migration/:/migration + - ./migration/:/migration networks: - hc-network environment: diff --git a/deployment/legacy-migration/volume-claim-mongo-export.yaml b/deployment/legacy-migration/volume-claim-mongo-export.yaml deleted file mode 100644 index 106ef4736..000000000 --- a/deployment/legacy-migration/volume-claim-mongo-export.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- - kind: PersistentVolumeClaim - apiVersion: v1 - metadata: - name: mongo-export-claim - namespace: human-connection - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 1Gi From 7f2df14ef3448abacc3a6f4e10a42a26b91b3e0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Wed, 24 Apr 2019 12:52:10 +0200 Subject: [PATCH 17/24] Update human connection specific configuration --- SUMMARY.md | 1 + deployment/.gitignore | 5 +- deployment/README.md | 40 +-------------- ...configmap.yaml => configmap.template.yaml} | 2 - deployment/human-connection/README.md | 50 +++++++++++++++++++ deployment/secrets.template.yaml | 1 + 6 files changed, 57 insertions(+), 42 deletions(-) rename deployment/{human-connection/configmap.yaml => configmap.template.yaml} (67%) create mode 100644 deployment/human-connection/README.md diff --git a/SUMMARY.md b/SUMMARY.md index 54a751238..24470f8fa 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -25,6 +25,7 @@ * [Digital Ocean](deployment/digital-ocean/README.md) * [Kubernetes Dashboard](deployment/digital-ocean/dashboard/README.md) * [HTTPS](deployment/digital-ocean/https/README.md) + * [Human Connection](deployment/human-connection/README.md) * [Neo4J DB Backup](deployment/backup.md) * [Legacy Migration](deployment/legacy-migration/README.md) * [Feature Specification](cypress/features.md) diff --git a/deployment/.gitignore b/deployment/.gitignore index aad0daea8..14cfa18ed 100644 --- a/deployment/.gitignore +++ b/deployment/.gitignore @@ -1,3 +1,4 @@ secrets.yaml -*/secrets.yaml -kubeconfig.yaml +configmap.yaml +**/secrets.yaml +**/configmap.yaml diff --git a/deployment/README.md b/deployment/README.md index e191bddc0..0615ccf9b 100644 --- a/deployment/README.md +++ b/deployment/README.md @@ -7,41 +7,5 @@ and get a kubernetes cluster. We have tested two different kubernetes providers: [Minikube](./minikube/README.md) and [Digital Ocean](./digital-ocean/README.md). - -## Installation with kubernetes - -You have to do some prerequisites e.g. change some secrets according to your own setup. - -### Edit secrets - -```bash -$ cp secrets.template.yaml human-connection/secrets.yaml -``` - -Change all secrets as needed. - -If you want to edit secrets, you have to `base64` encode them. See [kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/secret/#creating-a-secret-manually). - -```text -# example how to base64 a string: -$ echo -n 'admin' | base64 -YWRtaW4= -``` - -Those secrets get `base64` decoded in a kubernetes pod. - -### Create a namespace - -```text -$ kubectl apply -f namespace-human-connection.yaml -``` - -Switch to the namespace `human-connection` in your kubernetes dashboard. - -### Run the configuration - -```text -$ kubectl apply -f human-connection/ -``` - -This can take a while because kubernetes will download the docker images. Sit back and relax and have a look into your kubernetes dashboard. Wait until all pods turn green and they don't show a warning `Waiting: ContainerCreating` anymore. +Check out the specific documentation for your provider. After that, learn how +to apply the specific kubernetes configuration for [Human Connection](./human-connection/README.md). diff --git a/deployment/human-connection/configmap.yaml b/deployment/configmap.template.yaml similarity index 67% rename from deployment/human-connection/configmap.yaml rename to deployment/configmap.template.yaml index 5e4d6ba89..baf41661a 100644 --- a/deployment/human-connection/configmap.yaml +++ b/deployment/configmap.template.yaml @@ -9,8 +9,6 @@ NEO4J_USER: "neo4j" NEO4J_AUTH: "none" CLIENT_URI: "https://nitro-staging.human-connection.org" - MAPBOX_TOKEN: "pk.eyJ1IjoiaHVtYW4tY29ubmVjdGlvbiIsImEiOiJjajl0cnBubGoweTVlM3VwZ2lzNTNud3ZtIn0.KZ8KK9l70omjXbEkkbHGsQ" - PRIVATE_KEY_PASSPHRASE: "a7dsf78sadg87ad87sfagsadg78" metadata: name: configmap namespace: human-connection diff --git a/deployment/human-connection/README.md b/deployment/human-connection/README.md new file mode 100644 index 000000000..7df30e345 --- /dev/null +++ b/deployment/human-connection/README.md @@ -0,0 +1,50 @@ +# Kubernetes Configuration for Human Connection + +Deploying Human Connection with kubernetes is straight forward. All you have to +do is to change certain parameters, like domain names and API keys, then you +just apply our provided configuration files to your cluster. + +## Configuration + +Copy our provided templates: + +```bash +$ cp secrets.template.yaml human-connection/secrets.yaml +$ cp configmap.template.yaml human-connection/configmap.yaml +``` + +Change the `configmap.yaml` as needed, all variables will be available as +environment variables in your deployed kubernetes pods. + +If you want to edit secrets, you have to `base64` encode them. See [kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/secret/#creating-a-secret-manually). + +```text +# example how to base64 a string: +$ echo -n 'admin' | base64 --wrap 0 +YWRtaW4= +``` + +Those secrets get `base64` decoded and are available as environment variables in +your deployed kubernetes pods. + +## Create a namespace + +```text +$ kubectl apply -f namespace-human-connection.yaml +``` + +If you have a [kubernets dashboard](../digital-ocean/dashboard/README.md) +deployed you should switch to namespace `human-connection` in order to +monitor the state of your deployments. + +## Apply the configuration + +```text +# in folder deployment/ +$ kubectl apply -f human-connection/ +``` + +This can take a while because kubernetes will download the docker images. Sit +back and relax and have a look into your kubernetes dashboard. Wait until all +pods turn green and they don't show a warning `Waiting: ContainerCreating` +anymore. diff --git a/deployment/secrets.template.yaml b/deployment/secrets.template.yaml index ac56b7aa1..8f18dbf46 100644 --- a/deployment/secrets.template.yaml +++ b/deployment/secrets.template.yaml @@ -4,6 +4,7 @@ data: JWT_SECRET: "Yi8mJjdiNzhCRiZmdi9WZA==" MONGODB_PASSWORD: "TU9OR09EQl9QQVNTV09SRA==" PRIVATE_KEY_PASSPHRASE: "YTdkc2Y3OHNhZGc4N2FkODdzZmFnc2FkZzc4" + MAPBOX_TOKEN: "cGsuZXlKMUlqb2lhSFZ0WVc0dFkyOXVibVZqZEdsdmJpSXNJbUVpT2lKamFqbDBjbkJ1Ykdvd2VUVmxNM1Z3WjJsek5UTnVkM1p0SW4wLktaOEtLOWw3MG9talhiRWtrYkhHc1EK" metadata: name: human-connection namespace: human-connection From 783d34f53afb4dd32b366bf9810ace78d14f258e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Wed, 24 Apr 2019 13:04:03 +0200 Subject: [PATCH 18/24] Fix README for minikube --- deployment/digital-ocean/dashboard/README.md | 1 + deployment/minikube/README.md | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/deployment/digital-ocean/dashboard/README.md b/deployment/digital-ocean/dashboard/README.md index a2e5446b2..3ae6378bf 100644 --- a/deployment/digital-ocean/dashboard/README.md +++ b/deployment/digital-ocean/dashboard/README.md @@ -49,6 +49,7 @@ token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZ Grab the token from above and paste it into the [login screen](http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/) When you are logged in, you should see sth. like: + ![Dashboard](./dashboard-screenshot.png) Feel free to save the login token from above in your password manager. Unlike the `kubeconfig` file, this token does not expire. diff --git a/deployment/minikube/README.md b/deployment/minikube/README.md index f1c56d216..e77ddd667 100644 --- a/deployment/minikube/README.md +++ b/deployment/minikube/README.md @@ -2,9 +2,8 @@ There are many Kubernetes providers, but if you're just getting started, Minikube is a tool that you can use to get your feet wet. -[Install Minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/) - -Open minikube dashboard: +After you [installed Minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/) +open your minikube dashboard: ```text $ minikube dashboard @@ -12,9 +11,15 @@ $ minikube dashboard This will give you an overview. Some of the steps below need some timing to make ressources available to other dependent deployments. Keeping an eye on the dashboard is a great way to check that. -Follow the [installation instruction](deployment.md#installation-with-kubernetes) below. If all the pods and services have settled and everything looks green in your minikube dashboard, expose the `nitro-web` service on your host system with: +Follow the installation instruction for [Human Connection](../human-connection/README.md). +If all the pods and services have settled and everything looks green in your +minikube dashboard, expose the services you want on your host system. + +For example: ```text $ minikube service nitro-web --namespace=human-connection +# optionally +$ minikube service nitro-backend --namespace=human-connection ``` From 3edb5908498274abda87ef50f12ecf4743c3474c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Wed, 24 Apr 2019 22:48:39 +0200 Subject: [PATCH 19/24] Dedicate extra chapter to volumes --- SUMMARY.md | 1 + deployment/human-connection/README.md | 8 ++++ .../human-connection/deployment-backend.yaml | 12 ------ .../human-connection/deployment-neo4j.yaml | 12 ------ deployment/volumes/README.md | 42 +++++++++++++++++++ deployment/volumes/neo4j-data.yaml | 12 ++++++ deployment/volumes/uploads.yaml | 12 ++++++ 7 files changed, 75 insertions(+), 24 deletions(-) create mode 100644 deployment/volumes/README.md create mode 100644 deployment/volumes/neo4j-data.yaml create mode 100644 deployment/volumes/uploads.yaml diff --git a/SUMMARY.md b/SUMMARY.md index 24470f8fa..7c1e41d13 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -26,6 +26,7 @@ * [Kubernetes Dashboard](deployment/digital-ocean/dashboard/README.md) * [HTTPS](deployment/digital-ocean/https/README.md) * [Human Connection](deployment/human-connection/README.md) + * [Volumes](deployment/volumes/README.md) * [Neo4J DB Backup](deployment/backup.md) * [Legacy Migration](deployment/legacy-migration/README.md) * [Feature Specification](cypress/features.md) diff --git a/deployment/human-connection/README.md b/deployment/human-connection/README.md index 7df30e345..f281a4617 100644 --- a/deployment/human-connection/README.md +++ b/deployment/human-connection/README.md @@ -37,6 +37,14 @@ If you have a [kubernets dashboard](../digital-ocean/dashboard/README.md) deployed you should switch to namespace `human-connection` in order to monitor the state of your deployments. +## Create persistent volumes + +While the deployments and services can easily be restored, simply by deleting +and applying the kubernetes configurations again, certain data is not that +easily recovered. Therefore we separated persistent volumes from deployments +and services. There is a [dedicated section](../volumes/README.md). Create those +persistent volumes once before you apply the configuration. + ## Apply the configuration ```text diff --git a/deployment/human-connection/deployment-backend.yaml b/deployment/human-connection/deployment-backend.yaml index 29992ef7e..a873b7bb2 100644 --- a/deployment/human-connection/deployment-backend.yaml +++ b/deployment/human-connection/deployment-backend.yaml @@ -43,15 +43,3 @@ restartPolicy: Always terminationGracePeriodSeconds: 30 status: {} ---- - kind: PersistentVolumeClaim - apiVersion: v1 - metadata: - name: uploads-claim - namespace: human-connection - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 2Gi diff --git a/deployment/human-connection/deployment-neo4j.yaml b/deployment/human-connection/deployment-neo4j.yaml index 5b89b3656..4a715da76 100644 --- a/deployment/human-connection/deployment-neo4j.yaml +++ b/deployment/human-connection/deployment-neo4j.yaml @@ -57,15 +57,3 @@ claimName: neo4j-data-claim restartPolicy: Always terminationGracePeriodSeconds: 30 ---- - kind: PersistentVolumeClaim - apiVersion: v1 - metadata: - name: neo4j-data-claim - namespace: human-connection - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 1Gi diff --git a/deployment/volumes/README.md b/deployment/volumes/README.md new file mode 100644 index 000000000..b838794d5 --- /dev/null +++ b/deployment/volumes/README.md @@ -0,0 +1,42 @@ +# Persistent Volumes + +At the moment, the application needs two persistent volumes: + +* The `/data/` folder where `neo4j` stores its database and +* the folder `/nitro-backend/public/uploads` where the backend stores uploads. + +As a matter of precaution, the persistent volume claims that setup these volumes +live in a separate folder. You don't want to accidently loose all your data in +your database by running `kubectl delete -f human-connection/`, do you? + +## Create Persistent Volume Claims + +Run the following: +```sh +# in folder deployments/ +$ kubectl apply -f volumes +persistentvolumeclaim/neo4j-data-claim created +persistentvolumeclaim/uploads-claim created +``` + +## Change Reclaim Policy + +We recommend to change the `ReclaimPolicy`, so if you delete the persistent +volume claims, the associated volumes will be released, not deleted: + +```sh +$ kubectl --namespace=human-connection get pv + +NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE +pvc-bd02a715-66d0-11e9-be52-ba9c337f4551 1Gi RWO Delete Bound human-connection/neo4j-data-claim do-block-storage 4m24s +pvc-bd208086-66d0-11e9-be52-ba9c337f4551 2Gi RWO Delete Bound human-connection/uploads-claim do-block-storage 4m12s +``` + +Get the volume id from above, then change `ReclaimPolicy` with: +```sh +kubectl patch pv -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}' + +# in the above example +kubectl patch pv pvc-bd02a715-66d0-11e9-be52-ba9c337f4551 -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}' +kubectl patch pv pvc-bd208086-66d0-11e9-be52-ba9c337f4551 -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}' +``` diff --git a/deployment/volumes/neo4j-data.yaml b/deployment/volumes/neo4j-data.yaml new file mode 100644 index 000000000..f077be933 --- /dev/null +++ b/deployment/volumes/neo4j-data.yaml @@ -0,0 +1,12 @@ +--- + kind: PersistentVolumeClaim + apiVersion: v1 + metadata: + name: neo4j-data-claim + namespace: human-connection + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi diff --git a/deployment/volumes/uploads.yaml b/deployment/volumes/uploads.yaml new file mode 100644 index 000000000..11a8027e9 --- /dev/null +++ b/deployment/volumes/uploads.yaml @@ -0,0 +1,12 @@ +--- + kind: PersistentVolumeClaim + apiVersion: v1 + metadata: + name: uploads-claim + namespace: human-connection + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 2Gi From 86c0307ddb51fc305e0b063680245ec959e2dead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Wed, 24 Apr 2019 23:46:06 +0200 Subject: [PATCH 20/24] Reclaim volume claims for maintenance worker --- ...on-worker.yaml => maintenance-worker.yaml} | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) rename deployment/legacy-migration/{db-migration-worker.yaml => maintenance-worker.yaml} (65%) diff --git a/deployment/legacy-migration/db-migration-worker.yaml b/deployment/legacy-migration/maintenance-worker.yaml similarity index 65% rename from deployment/legacy-migration/db-migration-worker.yaml rename to deployment/legacy-migration/maintenance-worker.yaml index f983bd5bb..cda17400a 100644 --- a/deployment/legacy-migration/db-migration-worker.yaml +++ b/deployment/legacy-migration/maintenance-worker.yaml @@ -5,11 +5,6 @@ name: nitro-maintenance-worker namespace: human-connection spec: - volumes: - - name: secret-volume - secret: - secretName: ssh-keys - defaultMode: 0400 containers: - name: nitro-maintenance-worker image: humanconnection/maintenance-worker:latest @@ -22,3 +17,18 @@ - name: secret-volume readOnly: false mountPath: /root/.ssh + - name: uploads + mountPath: /nitro-backend/public/uploads + - name: neo4j-data + mountPath: /data/ + volumes: + - name: secret-volume + secret: + secretName: ssh-keys + defaultMode: 0400 + - name: uploads + persistentVolumeClaim: + claimName: uploads-claim + - name: neo4j-data + persistentVolumeClaim: + claimName: neo4j-data-claim From c350fb37a922aee81ac0cb2eedd2686f8522f98e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Wed, 24 Apr 2019 23:55:49 +0200 Subject: [PATCH 21/24] Fine tuning documentation --- deployment/human-connection/README.md | 6 +++--- deployment/legacy-migration/README.md | 17 ++++++++++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/deployment/human-connection/README.md b/deployment/human-connection/README.md index f281a4617..d6a2dd989 100644 --- a/deployment/human-connection/README.md +++ b/deployment/human-connection/README.md @@ -18,7 +18,7 @@ environment variables in your deployed kubernetes pods. If you want to edit secrets, you have to `base64` encode them. See [kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/secret/#creating-a-secret-manually). -```text +```bash # example how to base64 a string: $ echo -n 'admin' | base64 --wrap 0 YWRtaW4= @@ -29,7 +29,7 @@ your deployed kubernetes pods. ## Create a namespace -```text +```bash $ kubectl apply -f namespace-human-connection.yaml ``` @@ -47,7 +47,7 @@ persistent volumes once before you apply the configuration. ## Apply the configuration -```text +```bash # in folder deployment/ $ kubectl apply -f human-connection/ ``` diff --git a/deployment/legacy-migration/README.md b/deployment/legacy-migration/README.md index eeb613146..8a3870ab3 100644 --- a/deployment/legacy-migration/README.md +++ b/deployment/legacy-migration/README.md @@ -39,6 +39,7 @@ Bring the application into maintenance mode. Then temporarily delete backend and database deployments + ```bash $ kubectl --namespace=human-connection get deployments NAME READY UP-TO-DATE AVAILABLE AGE @@ -52,7 +53,8 @@ deployment.extensions "nitro-backend" deleted ``` Deploy one-time maintenance-worker pod: -``` + +```bash # in deployment/legacy-migration/ $ kubectl apply -f db-migration-worker.yaml pod/nitro-maintenance-worker created @@ -60,7 +62,7 @@ pod/nitro-maintenance-worker created Import legacy database and uploads: -```text +```bash $ kubectl --namespace=human-connection exec -it nitro-maintenance-worker bash $ import_legacy_db $ import_uploads @@ -68,7 +70,16 @@ $ exit ``` Delete the pod when you're done: -``` + +```bash $ kubectl --namespace=human-connection delete pod nitro-maintenance-worker ``` +Oh, and of course you have to get those deleted deployments back. One way of +doing it would be: + +```bash +# in folder deployment/ +$ kubectl apply -f human-connection/deployment-backend.yaml -f human-connection/deployment-neo4j.yaml +``` + From 473bf63946c9dc5d01a953517c85843b821671f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 25 Apr 2019 04:46:46 +0000 Subject: [PATCH 22/24] Bump eslint-plugin-jest from 22.4.1 to 22.5.0 in /backend Bumps [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) from 22.4.1 to 22.5.0. - [Release notes](https://github.com/jest-community/eslint-plugin-jest/releases) - [Changelog](https://github.com/jest-community/eslint-plugin-jest/blob/master/CHANGELOG.md) - [Commits](https://github.com/jest-community/eslint-plugin-jest/compare/v22.4.1...v22.5.0) Signed-off-by: dependabot[bot] --- backend/package.json | 2 +- backend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index 942cd8023..a258b1f27 100644 --- a/backend/package.json +++ b/backend/package.json @@ -85,7 +85,7 @@ "eslint": "~5.16.0", "eslint-config-standard": "~12.0.0", "eslint-plugin-import": "~2.17.2", - "eslint-plugin-jest": "~22.4.1", + "eslint-plugin-jest": "~22.5.0", "eslint-plugin-node": "~8.0.1", "eslint-plugin-promise": "~4.1.1", "eslint-plugin-standard": "~4.0.0", diff --git a/backend/yarn.lock b/backend/yarn.lock index b7cf8099b..e66d93297 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -3030,10 +3030,10 @@ eslint-plugin-import@~2.17.2: read-pkg-up "^2.0.0" resolve "^1.10.0" -eslint-plugin-jest@~22.4.1: - version "22.4.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.4.1.tgz#a5fd6f7a2a41388d16f527073b778013c5189a9c" - integrity sha512-gcLfn6P2PrFAVx3AobaOzlIEevpAEf9chTpFZz7bYfc7pz8XRv7vuKTIE4hxPKZSha6XWKKplDQ0x9Pq8xX2mg== +eslint-plugin-jest@~22.5.0: + version "22.5.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.5.0.tgz#3a02527a5b08f7232f7bb0a52da98407bf84cdd0" + integrity sha512-YLeCRAuU3qP9lRZMul1/IbxXGg1THVpWFPBEa+VUQkcqEtO3W9GDKZ84MxYxzKTwMChTjj1l2vuNKva8HYtGPg== eslint-plugin-node@~8.0.1: version "8.0.1" From 418b93e4fd940d7e3fcdee474d6a8c21961f87f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 25 Apr 2019 04:54:26 +0000 Subject: [PATCH 23/24] Bump tippy.js from 4.2.1 to 4.3.0 in /webapp Bumps [tippy.js](https://github.com/atomiks/tippyjs) from 4.2.1 to 4.3.0. - [Release notes](https://github.com/atomiks/tippyjs/releases) - [Commits](https://github.com/atomiks/tippyjs/compare/v4.2.1...v4.3.0) Signed-off-by: dependabot[bot] --- webapp/package.json | 2 +- webapp/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index 58adc74a2..80a0ff427 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -80,7 +80,7 @@ "nodemon": "~1.18.11", "prettier": "~1.14.3", "sass-loader": "~7.1.0", - "tippy.js": "^4.2.1", + "tippy.js": "^4.3.0", "vue-jest": "~3.0.4", "vue-svg-loader": "~0.12.0" } diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 1059cfb53..5b7364d2f 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -10355,10 +10355,10 @@ timsort@^0.3.0: resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= -tippy.js@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-4.2.1.tgz#9e4939d976465f77229b05a3cb233b5dc28cf850" - integrity sha512-xEE7zYNgQxCDdPcuT6T04f0frPh0wO7CcIqJKMFazU/NqusyjCgYSkLRosIHoiRkZMRzSPOudC8wRN5GjvAyOQ== +tippy.js@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-4.3.0.tgz#5f661fed7fa30c90609eb87f6657005dd041ede3" + integrity sha512-SjctzIfkx3+waue+Ew58MMTuzYD4SK9wJOnCEdrCmwZiKJ7chZSxOguFmBm11tmTlZuGbxncUC/5Qu6GqzD2qQ== dependencies: popper.js "^1.14.7" From 43ac10f7d7d729f2a8f1f39e366f009007914d24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Thu, 25 Apr 2019 11:43:24 +0200 Subject: [PATCH 24/24] Nice catch @Tirokk --- deployment/legacy-migration/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/legacy-migration/README.md b/deployment/legacy-migration/README.md index 8a3870ab3..8cc7bd746 100644 --- a/deployment/legacy-migration/README.md +++ b/deployment/legacy-migration/README.md @@ -18,7 +18,7 @@ $ kubectl create configmap maintenance-worker \ --from-literal=MONGODB_PASSWORD=secretpassword \ --from-literal=MONGODB_AUTH_DB=hc_api \ --from-literal=MONGODB_DATABASE=hc_api \ - --from-literal=UPLOADS_DIRECTORY=/var/www/api/uploads \ + --from-literal=UPLOADS_DIRECTORY=/var/www/api/uploads ``` Create a secret with your public and private ssh keys. As the [kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/secret/#use-case-pod-with-ssh-keys) points out, you should be careful with your ssh keys. Anyone with access to your cluster will have access to your ssh keys. Better create a new pair with `ssh-keygen` and copy the public key to your legacy server with `ssh-copy-id`: