diff --git a/.github/workflows/containerbuild.yml b/.github/workflows/containerbuild.yml index 7b367f0eb0..68f1be456c 100644 --- a/.github/workflows/containerbuild.yml +++ b/.github/workflows/containerbuild.yml @@ -79,7 +79,7 @@ jobs: with: skip_after_successful_duplicate: false github_token: ${{ github.token }} - paths: '["k8s/images/nginx/*", ".github/workflows/containerbuild.yml"]' + paths: '["docker/Dockerfile.nginx.prod", "nginx/*", ".github/workflows/containerbuild.yml"]' build_nginx: name: nginx - test build of nginx Docker image @@ -100,6 +100,6 @@ jobs: uses: docker/build-push-action@v6 with: context: ./ - file: ./k8s/images/nginx/Dockerfile + file: ./docker/Dockerfile.nginx.prod platforms: linux/amd64 push: false diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index fc49a89d8a..0000000000 --- a/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "kolibri"] - path = kolibri - url = https://github.com/learningequality/kolibri.git -[submodule "contentcuration/kolibri"] - path = contentcuration/kolibri - url = https://github.com/learningequality/kolibri.git diff --git a/Makefile b/Makefile index 002d337323..dc1e70b51e 100644 --- a/Makefile +++ b/Makefile @@ -171,11 +171,7 @@ dcbuild: $(DOCKER_COMPOSE) build dcup: .docker/minio .docker/postgres - # run all services except for cloudprober - $(DOCKER_COMPOSE) up studio-app celery-worker - -dcup-cloudprober: .docker/minio .docker/postgres - # run all services including cloudprober + # run all services $(DOCKER_COMPOSE) up dcdown: diff --git a/cloudbuild-pr.yaml b/cloudbuild-pr.yaml deleted file mode 100644 index 2fb21ce2c5..0000000000 --- a/cloudbuild-pr.yaml +++ /dev/null @@ -1,102 +0,0 @@ -steps: -- name: 'gcr.io/cloud-builders/docker' - id: pull-app-image-cache - args: ['pull', 'gcr.io/$PROJECT_ID/learningequality-studio-app:latest'] - -- name: 'gcr.io/cloud-builders/docker' - id: build-app-image - waitFor: ['pull-app-image-cache'] # don't wait for previous steps - args: [ - 'build', - '-f', 'docker/Dockerfile.demo', - '--cache-from', 'gcr.io/$PROJECT_ID/learningequality-studio-app:latest', - '-t', 'gcr.io/$PROJECT_ID/learningequality-studio-app:$COMMIT_SHA', - '-t', 'gcr.io/$PROJECT_ID/learningequality-studio-app:latest', - '.' - ] - -- name: 'gcr.io/cloud-builders/docker' - id: build-nginx-image - waitFor: ['-'] # don't wait for previous steps - args: [ - 'build', - '-f', 'k8s/images/nginx/Dockerfile', - '--cache-from', 'gcr.io/$PROJECT_ID/learningequality-studio-nginx:latest', - '-t', 'gcr.io/$PROJECT_ID/learningequality-studio-nginx:$COMMIT_SHA', - '-t', 'gcr.io/$PROJECT_ID/learningequality-studio-nginx:latest', - '.' - ] - -- name: 'gcr.io/cloud-builders/docker' - id: push-app-image - waitFor: ['build-app-image'] - args: ['push', 'gcr.io/$PROJECT_ID/learningequality-studio-app:$COMMIT_SHA'] - -- name: 'gcr.io/cloud-builders/docker' - id: push-nginx-image - waitFor: ['build-nginx-image'] - args: ['push', 'gcr.io/$PROJECT_ID/learningequality-studio-nginx:$COMMIT_SHA'] - -- name: 'gcr.io/cloud-builders/gcloud' - id: decrypt-gcs-service-account - waitFor: ['-'] - args: [ - 'kms', 'decrypt', - '--location=global', '--keyring=builder-secrets', '--key=secret-encrypter', - '--ciphertext-file=k8s/build-secrets/$PROJECT_ID-gcs-service-account.json.enc', - '--plaintext-file=gcs-service-account.json' - ] - -- name: 'gcr.io/cloud-builders/gcloud' - id: create-new-database - waitFor: ['-'] - dir: "k8s" - entrypoint: 'bash' - args: [ - '-c', - './create-cloudsql-database.sh $_RELEASE_NAME $_DATABASE_INSTANCE_NAME' - ] - -- name: 'gcr.io/$PROJECT_ID/helm' - id: helm-deploy-studio-instance - waitFor: ['decrypt-gcs-service-account', 'push-app-image', 'push-nginx-image'] - dir: "k8s" - env: - - 'CLOUDSDK_COMPUTE_ZONE=us-central1-f' - - 'CLOUDSDK_CONTAINER_CLUSTER=dev-qa-cluster' - secretEnv: ['POSTMARK_API_KEY'] - entrypoint: 'bash' - args: - - -c - - > - /builder/helm.bash && - ./helm-deploy.sh - $_RELEASE_NAME - $_STORAGE_BUCKET - $COMMIT_SHA - $$POSTMARK_API_KEY - "" - "" - $_POSTGRES_USERNAME - $_RELEASE_NAME - $_POSTGRES_PASSWORD - $PROJECT_ID-$_DATABASE_INSTANCE_NAME-sql-proxy-gcloud-sqlproxy.sqlproxy - ../gcs-service-account.json - $PROJECT_ID - -- name: 'gcr.io/cloud-builders/gsutil' - id: remove-tarball-in-gcs - waitFor: ['helm-deploy-studio-instance'] - args: ['rm', $_TARBALL_LOCATION] - -timeout: 3600s -secrets: -- kmsKeyName: projects/ops-central/locations/global/keyRings/builder-secrets/cryptoKeys/secret-encrypter - secretEnv: - POSTMARK_API_KEY: CiQA7z1GH3QhvCEWNn6KS64t/c8BEQng5I4CdMC6VGNxJkWmZrwSTgB+R8mv/PSrzlDmCYSOZc4bugWA+K+lJ8nIll1BBsZZEV5M9GuOCYVn6sVWg9pCIVujwyb4EvEy1QaKmZCzAnTw9aHEXDH0sruAUHBaTA== - -images: - - 'gcr.io/$PROJECT_ID/learningequality-studio-nginx:$COMMIT_SHA' - - 'gcr.io/$PROJECT_ID/learningequality-studio-nginx:latest' - - 'gcr.io/$PROJECT_ID/learningequality-studio-app:$COMMIT_SHA' - - 'gcr.io/$PROJECT_ID/learningequality-studio-app:latest' diff --git a/cloudbuild-production.yaml b/cloudbuild-production.yaml deleted file mode 100644 index 3ff333a67f..0000000000 --- a/cloudbuild-production.yaml +++ /dev/null @@ -1,99 +0,0 @@ -steps: -- name: 'gcr.io/cloud-builders/docker' - id: pull-app-image-cache - args: ['pull', 'gcr.io/$PROJECT_ID/learningequality-studio-app:latest'] - -- name: 'gcr.io/cloud-builders/docker' - id: build-app-image - entrypoint: bash - waitFor: ['pull-app-image-cache'] # wait for app image cache pull to finish - args: - - -c - - > - docker build - --build_arg COMMIT_SHA=$COMMIT_SHA - -f k8s/images/app/Dockerfile - --cache-from gcr.io/$PROJECT_ID/learningequality-studio-app:latest - -t gcr.io/$PROJECT_ID/learningequality-studio-app:$COMMIT_SHA - -t gcr.io/$PROJECT_ID/learningequality-studio-app:latest - . - -- name: 'gcr.io/cloud-builders/docker' - id: build-nginx-image - waitFor: ['-'] # don't wait for previous steps - args: [ - 'build', - '-f', 'k8s/images/nginx/Dockerfile', - '--cache-from', 'gcr.io/$PROJECT_ID/learningequality-studio-nginx:latest', - '-t', 'gcr.io/$PROJECT_ID/learningequality-studio-nginx:$COMMIT_SHA', - '-t', 'gcr.io/$PROJECT_ID/learningequality-studio-nginx:latest', - '.' - ] - -- name: 'gcr.io/cloud-builders/docker' - id: pull-prober-image-cache - waitFor: ['-'] - args: ['pull', 'gcr.io/$PROJECT_ID/learningequality-studio-prober:latest'] - -- name: 'gcr.io/cloud-builders/docker' - id: build-prober-image - waitFor: ['pull-prober-image-cache'] # don't wait for previous steps - args: [ - 'build', - '-f', 'k8s/images/prober/Dockerfile', - '--cache-from', 'gcr.io/$PROJECT_ID/learningequality-studio-prober:latest', - '-t', 'gcr.io/$PROJECT_ID/learningequality-studio-prober:$COMMIT_SHA', - '-t', 'gcr.io/$PROJECT_ID/learningequality-studio-prober:latest', - '.' - ] - -- name: 'gcr.io/cloud-builders/docker' - id: push-app-image - waitFor: ['build-app-image'] - args: ['push', 'gcr.io/$PROJECT_ID/learningequality-studio-app:$COMMIT_SHA'] - -- name: 'gcr.io/cloud-builders/docker' - id: push-nginx-image - waitFor: ['build-nginx-image'] - args: ['push', 'gcr.io/$PROJECT_ID/learningequality-studio-nginx:$COMMIT_SHA'] - -- name: 'gcr.io/cloud-builders/docker' - id: push-prober-image - waitFor: ['build-prober-image'] - args: ['push', 'gcr.io/$PROJECT_ID/learningequality-studio-prober:$COMMIT_SHA'] - -- name: 'gcr.io/$PROJECT_ID/helm' - id: helm-deploy-studio-instance - waitFor: ['push-app-image', 'push-nginx-image'] - dir: "k8s" - env: - - 'CLOUDSDK_COMPUTE_ZONE=us-central1-f' - - 'CLOUDSDK_CONTAINER_CLUSTER=contentworkshop-central' - entrypoint: 'bash' - args: - - -c - - > - /builder/helm.bash && - ./helm-deploy.sh - $BRANCH_NAME - gcr.io/$PROJECT_ID/learningequality-studio-app:$COMMIT_SHA - gcr.io/$PROJECT_ID/learningequality-studio-nginx:$COMMIT_SHA - $_STORAGE_BUCKET - $COMMIT_SHA - $PROJECT_ID - $_DATABASE_INSTANCE_NAME - us-central1 - - -substitutions: - _DATABASE_INSTANCE_NAME: develop # by default, connect to the develop DB - _STORAGE_BUCKET: develop-studio-content - -timeout: 3600s -images: - - gcr.io/$PROJECT_ID/learningequality-studio-nginx:latest - - gcr.io/$PROJECT_ID/learningequality-studio-nginx:$COMMIT_SHA - - gcr.io/$PROJECT_ID/learningequality-studio-app:latest - - gcr.io/$PROJECT_ID/learningequality-studio-app:$COMMIT_SHA - - 'gcr.io/$PROJECT_ID/learningequality-studio-prober:$COMMIT_SHA' - - 'gcr.io/$PROJECT_ID/learningequality-studio-prober:latest' diff --git a/deploy/cloudprober.cfg b/deploy/cloudprober.cfg deleted file mode 100644 index c5a129455e..0000000000 --- a/deploy/cloudprober.cfg +++ /dev/null @@ -1,187 +0,0 @@ -probe { - name: "google_homepage" - type: HTTP - targets { - host_names: "www.google.com" - } - interval_msec: 60000 # 60s - timeout_msec: 1000 # 1s -} - -probe { - name: "facebook_homepage" - type: HTTP - targets { - host_names: "www.facebook.com" - } - interval_msec: 60000 # 60s - timeout_msec: 1000 # 1s -} - -probe { - name: "studio_homepage" - type: HTTP - targets { - host_names: "studio.learningequality.org" - } - interval_msec: 60000 # 60s - timeout_msec: 1000 # 1s -} - -probe { - name: "login" - type: EXTERNAL - targets { dummy_targets {} } - external_probe { - mode: ONCE - command: "./probers/login_page_probe.py" - } - interval_msec: 60000 # 60s - timeout_msec: 1000 # 1s -} - -probe { - name: "postgres" - type: EXTERNAL - targets { dummy_targets {} } - external_probe { - mode: ONCE - command: "./probers/postgres_probe.py" - } - interval_msec: 60000 # 60s - timeout_msec: 1000 # 1s -} - -probe { - name: "workers" - type: EXTERNAL - targets { dummy_targets {} } - external_probe { - mode: ONCE - command: "./probers/worker_probe.py" - } - interval_msec: 60000 # 60s - timeout_msec: 5000 # 5s -} - -probe { - name: "channel_creation" - type: EXTERNAL - targets { dummy_targets {} } - external_probe { - mode: ONCE - command: "./probers/channel_creation_probe.py" - } - interval_msec: 300000 # 5mins - timeout_msec: 10000 # 10s -} - -probe { - name: "channel_update" - type: EXTERNAL - targets { dummy_targets {} } - external_probe { - mode: ONCE - command: "./probers/channel_update_probe.py" - } - interval_msec: 60000 # 1min - timeout_msec: 10000 # 10s -} - -probe { - name: "channel_edit_page" - type: EXTERNAL - targets { dummy_targets {} } - external_probe { - mode: ONCE - command: "./probers/channel_edit_page_probe.py" - } - interval_msec: 10000 # 10s - timeout_msec: 10000 # 10s -} - -probe { - name: "postgres_read_contentnode" - type: EXTERNAL - targets { dummy_targets {} } - external_probe { - mode: ONCE - command: "./probers/postgres_read_contentnode_probe.py" - } - interval_msec: 60000 # 60s - timeout_msec: 1000 # 1s -} - -probe { - name: "postgres_write_contentnode" - type: EXTERNAL - targets { dummy_targets {} } - external_probe { - mode: ONCE - command: "./probers/postgres_write_contentnode_probe.py" - } - interval_msec: 60000 # 60s - timeout_msec: 1000 # 1s -} - -probe { - name: "topic_creation" - type: EXTERNAL - targets { dummy_targets {} } - external_probe { - mode: ONCE - command: "./probers/topic_creation_probe.py" - } - interval_msec: 300000 # 5mins - timeout_msec: 20000 # 20s -} - -probe { - name: "postmark_api" - type: EXTERNAL - targets { dummy_targets {} } - external_probe { - mode: ONCE - command: "./probers/postmark_api_probe.py" - } - interval_msec: 300000 # 5 minutes - timeout_msec: 5000 # 5s -} - -probe { - name: "publishing_status" - type: EXTERNAL - targets { dummy_targets {} } - external_probe { - mode: ONCE - command: "./probers/publishing_status_probe.py" - } - interval_msec: 3600000 # 1 hour - timeout_msec: 10000 # 10s -} - -probe { - name: "unapplied_changes_status" - type: EXTERNAL - targets { dummy_targets {} } - external_probe { - mode: ONCE - command: "./probers/unapplied_changes_probe.py" - } - interval_msec: 1800000 # 30 minutes - timeout_msec: 20000 # 20s -} - -probe { - name: "task_queue_status" - type: EXTERNAL - targets { dummy_targets {} } - external_probe { - mode: ONCE - command: "./probers/task_queue_probe.py" - } - interval_msec: 600000 # 10 minutes - timeout_msec: 10000 # 10s -} - -# Note: When deploying on GKE, the error logs can be found under GCE VM instance. diff --git a/deploy/prober-entrypoint.sh b/deploy/prober-entrypoint.sh deleted file mode 100755 index 323e03cab0..0000000000 --- a/deploy/prober-entrypoint.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -curl -L -o cloudprober.zip https://github.com/google/cloudprober/releases/download/v0.10.2/cloudprober-v0.10.2-linux-x86_64.zip -unzip -p cloudprober.zip > /bin/cloudprober -chmod +x /bin/cloudprober - -cd deploy/ -cloudprober -logtostderr -config_file cloudprober.cfg diff --git a/deploy/probers/base.py b/deploy/probers/base.py deleted file mode 100644 index 7f85a18c16..0000000000 --- a/deploy/probers/base.py +++ /dev/null @@ -1,112 +0,0 @@ -import datetime -import os - -import requests - -USERNAME = os.getenv("PROBER_STUDIO_USERNAME") or "a@a.com" -PASSWORD = os.getenv("PROBER_STUDIO_PASSWORD") or "a" -PRODUCTION_MODE_ON = os.getenv("PROBER_STUDIO_PRODUCTION_MODE_ON") or False -STUDIO_BASE_URL = os.getenv("PROBER_STUDIO_BASE_URL") or "http://127.0.0.1:8080" - - -class BaseProbe(object): - - metric = "STUB_METRIC" - develop_only = False - prober_name = "PROBER" - - def __init__(self): - self.session = requests.Session() - self.session.headers.update( - {"User-Agent": "Studio-Internal-Prober={}".format(self.prober_name)} - ) - - def do_probe(self): - pass - - def _login(self): - # get our initial csrf - url = self._construct_studio_url("/en/accounts/") - r = self.session.get(url) - r.raise_for_status() - csrf = self.session.cookies.get("csrftoken") - formdata = { - "username": USERNAME, - "password": PASSWORD, - } - headers = { - "referer": url, - "X-Studio-Internal-Prober": "LOGIN-PROBER", - "X-CSRFToken": csrf, - } - - r = self.session.post( - self._construct_studio_url("/en/accounts/login/"), - json=formdata, - headers=headers, - allow_redirects=False, - ) - r.raise_for_status() - - # Since logging into Studio with correct username and password should redirect, fail otherwise - if r.status_code != 302: - raise ProberException("Cannot log into Studio.") - - return r - - def _construct_studio_url(self, path): - path_stripped = path.lstrip("/") - url = "{base_url}/{path}".format(base_url=STUDIO_BASE_URL, path=path_stripped) - return url - - def request( - self, - path, - action="GET", - data=None, - headers=None, - contenttype="application/json", - ): - data = data or {} - headers = headers or {} - - # Make sure session is logged in - if not self.session.cookies.get("csrftoken"): - self._login() - - url = self._construct_studio_url(path) - - headers.update( - { - "X-CSRFToken": self.session.cookies.get("csrftoken"), - } - ) - - headers.update({"Content-Type": contenttype}) - headers.update({"X-Studio-Internal-Prober": self.prober_name}) - response = self.session.request(action, url, data=data, headers=headers) - response.raise_for_status() - - return response - - def run(self): - - if self.develop_only and PRODUCTION_MODE_ON: - return - - start_time = datetime.datetime.now() - - self.do_probe() - - end_time = datetime.datetime.now() - elapsed = (end_time - start_time).total_seconds() * 1000 - - print( # noqa: T201 - "{metric_name} {latency_ms}".format( - metric_name=self.metric, latency_ms=elapsed - ) - ) - - -class ProberException(Exception): - pass diff --git a/deploy/probers/channel_creation_probe.py b/deploy/probers/channel_creation_probe.py deleted file mode 100755 index b7ab8d4254..0000000000 --- a/deploy/probers/channel_creation_probe.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python -import json - -from base import BaseProbe - - -class ChannelCreationProbe(BaseProbe): - - metric = "channel_creation_latency_msec" - develop_only = True - prober_name = "CHANNEL-CREATION-PROBER" - - def _get_user_id(self): - response = self.request("api/internal/authenticate_user_internal") - return json.loads(response.content)["user_id"] - - def do_probe(self): - payload = { - "description": "description", - "language": "en-PT", - "name": "test", - "thumbnail": "b3897c3d96bde7f1cff77ce368924098.png", - "content_defaults": "{}", - "editors": [self._get_user_id()], - } - self.request( - "api/channel", - action="POST", - data=payload, - contenttype="application/x-www-form-urlencoded", - ) - - -if __name__ == "__main__": - ChannelCreationProbe().run() diff --git a/deploy/probers/channel_edit_page_probe.py b/deploy/probers/channel_edit_page_probe.py deleted file mode 100755 index 2b3b80d2a3..0000000000 --- a/deploy/probers/channel_edit_page_probe.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python -import json - -from base import BaseProbe - - -class ChannelEditPageProbe(BaseProbe): - - metric = "channel_edit_page_latency_msec" - prober_name = "CHANNEL-EDIT-PAGE-PROBER" - - def _get_channel(self): - response = self.request("api/probers/get_prober_channel") - return json.loads(response.content) - - def do_probe(self): - channel = self._get_channel() - path = "channels/{}/edit".format(channel["id"]) - self.request(path) - - -if __name__ == "__main__": - ChannelEditPageProbe().run() diff --git a/deploy/probers/channel_update_probe.py b/deploy/probers/channel_update_probe.py deleted file mode 100755 index 1951df9348..0000000000 --- a/deploy/probers/channel_update_probe.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python -import json - -from base import BaseProbe - - -class ChannelUpdateProbe(BaseProbe): - - metric = "channel_update_latency_msec" - prober_name = "CHANNEL-UPDATE-PROBER" - develop_only = True - - def _get_channel(self): - response = self.request("api/probers/get_prober_channel") - return json.loads(response.content) - - def do_probe(self): - channel = self._get_channel() - payload = {"name": "New Test Name", "id": channel["id"]} - path = "api/channel/{}".format(channel["id"]) - self.request( - path, - action="PATCH", - data=payload, - contenttype="application/x-www-form-urlencoded", - ) - - -if __name__ == "__main__": - ChannelUpdateProbe().run() diff --git a/deploy/probers/login_page_probe.py b/deploy/probers/login_page_probe.py deleted file mode 100755 index 42ed9a43e3..0000000000 --- a/deploy/probers/login_page_probe.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python -from base import BaseProbe - - -class LoginProbe(BaseProbe): - - metric = "login_latency_msec" - - def do_probe(self): - self._login() - - -if __name__ == "__main__": - LoginProbe().run() diff --git a/deploy/probers/postgres_probe.py b/deploy/probers/postgres_probe.py deleted file mode 100755 index 3aa29acc0c..0000000000 --- a/deploy/probers/postgres_probe.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python -import os - -import psycopg2 -from base import BaseProbe - - -# Use dev options if no env set -DB_HOST = os.getenv("DATA_DB_HOST") or "localhost" -DB_PORT = 5432 -DB_NAME = os.getenv("DATA_DB_NAME") or "kolibri-studio" -DB_USER = os.getenv("DATA_DB_USER") or "learningequality" -DB_PASSWORD = os.getenv("DATA_DB_PASS") or "kolibri" -TIMEOUT_SECONDS = 2 - - -class PostgresProbe(BaseProbe): - metric = "postgres_latency_msec" - - def do_probe(self): - conn = psycopg2.connect( - host=DB_HOST, - port=DB_PORT, - dbname=DB_NAME, - user=DB_USER, - password=DB_PASSWORD, - connect_timeout=TIMEOUT_SECONDS, - ) - cur = conn.cursor() - cur.execute("SELECT datname FROM pg_database;") - cur.fetchone() # raises exception if cur.execute() produced no results - conn.close() - - -if __name__ == "__main__": - PostgresProbe().run() diff --git a/deploy/probers/postgres_read_contentnode_probe.py b/deploy/probers/postgres_read_contentnode_probe.py deleted file mode 100755 index fa4767f404..0000000000 --- a/deploy/probers/postgres_read_contentnode_probe.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python -import os - -import psycopg2 -from base import BaseProbe - - -# Use dev options if no env set -DB_HOST = os.getenv("DATA_DB_HOST") or "localhost" -DB_PORT = 5432 -DB_NAME = os.getenv("DATA_DB_NAME") or "kolibri-studio" -DB_USER = os.getenv("DATA_DB_USER") or "learningequality" -DB_PASSWORD = os.getenv("DATA_DB_PASS") or "kolibri" -TIMEOUT_SECONDS = 2 - - -class PostgresReadContentnodeProbe(BaseProbe): - metric = "postgres_read_contentnode_latency_msec" - - def do_probe(self): - conn = psycopg2.connect( - host=DB_HOST, - port=DB_PORT, - dbname=DB_NAME, - user=DB_USER, - password=DB_PASSWORD, - connect_timeout=TIMEOUT_SECONDS, - ) - cur = conn.cursor() - cur.execute("SELECT * FROM contentcuration_contentnode LIMIT 1;") - num = cur.fetchone() - conn.close() - if not num: - raise Exception("Reading a ContentNode in PostgreSQL database failed.") - - -if __name__ == "__main__": - PostgresReadContentnodeProbe().run() diff --git a/deploy/probers/postgres_write_contentnode_probe.py b/deploy/probers/postgres_write_contentnode_probe.py deleted file mode 100755 index 7785116fe4..0000000000 --- a/deploy/probers/postgres_write_contentnode_probe.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python -import os -from datetime import datetime - -import psycopg2 -from base import BaseProbe - -# Use dev options if no env set -DB_HOST = os.getenv("DATA_DB_HOST") or "localhost" -DB_PORT = 5432 -DB_NAME = os.getenv("DATA_DB_NAME") or "kolibri-studio" -DB_USER = os.getenv("DATA_DB_USER") or "learningequality" -DB_PASSWORD = os.getenv("DATA_DB_PASS") or "kolibri" -TIMEOUT_SECONDS = 2 - - -class PostgresWriteContentnodeProbe(BaseProbe): - metric = "postgres_write_contentnode_latency_msec" - - develop_only = True - - def do_probe(self): - conn = psycopg2.connect( - host=DB_HOST, - port=DB_PORT, - dbname=DB_NAME, - user=DB_USER, - password=DB_PASSWORD, - connect_timeout=TIMEOUT_SECONDS, - ) - cur = conn.cursor() - now = datetime.now() - cur.execute( - """ - INSERT INTO contentcuration_contentnode(id, content_id, kind_id, title, description,sort_order, created, - modified, changed, lft, rght, tree_id, level, published, node_id, freeze_authoring_data, publishing, role_visibility) - VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s); - """, - ( - "testpostgreswriteprobe", - "testprobecontentid", - "topic", - "test postgres write contentnode probe", - "test postgres write contentnode probe", - 1, - now, - now, - True, - 1, - 1, - 1, - 1, - False, - "testprobenodeid", - False, - False, - "test", - ), - ) - conn.close() - - -if __name__ == "__main__": - PostgresWriteContentnodeProbe().run() diff --git a/deploy/probers/postmark_api_probe.py b/deploy/probers/postmark_api_probe.py deleted file mode 100755 index 30cbb1741c..0000000000 --- a/deploy/probers/postmark_api_probe.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python -import requests -from base import BaseProbe - -POSTMARK_SERVICE_STATUS_URL = "https://status.postmarkapp.com/api/1.0/services" - -# (See here for API details: https://status.postmarkapp.com/api) -ALL_POSSIBLE_STATUSES = ["UP", "MAINTENANCE", "DELAY", "DEGRADED", "DOWN"] - -PASSING_POSTMARK_STATUSES = { - "/services/smtp": ["UP", "MAINTENANCE"], - "/services/api": ALL_POSSIBLE_STATUSES, - "/services/inbound": ALL_POSSIBLE_STATUSES, - "/services/web": ALL_POSSIBLE_STATUSES, -} - - -class PostmarkProbe(BaseProbe): - metric = "postmark_api_latency_msec" - - def do_probe(self): - r = requests.get(url=POSTMARK_SERVICE_STATUS_URL) - for service in r.json(): - allowed_statuses = PASSING_POSTMARK_STATUSES.get(service["url"]) - passing = service["status"] in allowed_statuses - - if passing: - continue - raise Exception( - "Postmark's `%s` service has status %s, but we require one of the following: %s" - % (service["name"], service["status"], allowed_statuses) - ) - - -if __name__ == "__main__": - PostmarkProbe().run() diff --git a/deploy/probers/publishing_status_probe.py b/deploy/probers/publishing_status_probe.py deleted file mode 100755 index fffe67eb92..0000000000 --- a/deploy/probers/publishing_status_probe.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python -import datetime -import os - -from base import BaseProbe -from base import ProberException -from base import PRODUCTION_MODE_ON - - -ALERT_THRESHOLD = int( - os.getenv("PROBER_PUBLISHING_ALERT_THRESHOLD") or 2 * 3600 -) # default = 2 hours -DATE_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ" - - -class PublishingStatusProbe(BaseProbe): - - metric = "max_publishing_duration_sec" - prober_name = "PUBLISHING_STATUS_PROBER" - - def run(self): - if self.develop_only and PRODUCTION_MODE_ON: - return - - r = self.request("api/probers/publishing_status/") - results = r.json() - now = datetime.datetime.now() - max_duration = 0 - channel_ids = [] - - for result in results: - duration = ( - now - datetime.datetime.strptime(result["performed"], DATE_FORMAT) - ).seconds - max_duration = max(max_duration, duration) - if duration >= ALERT_THRESHOLD or not result["task_id"]: - channel_ids.append(result["channel_id"]) - - if max_duration > 0: - print( # noqa: T201 - "{metric_name} {duration_sec}".format( - metric_name=self.metric, duration_sec=max_duration - ) - ) - - if channel_ids: - raise ProberException( - "Publishing alert for channels: {}".format(", ".join(channel_ids)) - ) - - -if __name__ == "__main__": - PublishingStatusProbe().run() diff --git a/deploy/probers/task_queue_probe.py b/deploy/probers/task_queue_probe.py deleted file mode 100755 index 6148176856..0000000000 --- a/deploy/probers/task_queue_probe.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python -from base import BaseProbe - - -class TaskQueueProbe(BaseProbe): - - metric = "task_queue_ping_latency_msec" - threshold = 50 - - def do_probe(self): - r = self.request("api/probers/task_queue_status/") - r.raise_for_status() - results = r.json() - - task_count = results.get("queued_task_count", 0) - if task_count >= self.threshold: - raise Exception( - "Task queue length is over threshold! {} > {}".format( - task_count, self.threshold - ) - ) - - -if __name__ == "__main__": - TaskQueueProbe().run() diff --git a/deploy/probers/topic_creation_probe.py b/deploy/probers/topic_creation_probe.py deleted file mode 100755 index 6c7090c598..0000000000 --- a/deploy/probers/topic_creation_probe.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python -import json - -from base import BaseProbe -from le_utils.constants import content_kinds - - -class TopicCreationProbe(BaseProbe): - - metric = "topic_creation_latency_msec" - develop_only = True - prober_name = "TOPIC-CREATION-PROBER" - - def _get_channel(self): - response = self.request("api/probers/get_prober_channel") - return json.loads(response.content) - - def do_probe(self): - channel = self._get_channel() - payload = { - "title": "Statistics and Probeability", - "kind": content_kinds.TOPIC, - } - response = self.request( - "api/contentnode", action="POST", data=json.dumps(payload) - ) - - # Test saving to channel works - new_topic = json.loads(response.content) - new_topic.update({"parent": channel["main_tree"]}) - path = "api/contentnode/{}".format(new_topic["id"]) - self.request( - path, - action="PUT", - data=payload, - contenttype="application/x-www-form-urlencoded", - ) - - -if __name__ == "__main__": - TopicCreationProbe().run() diff --git a/deploy/probers/unapplied_changes_probe.py b/deploy/probers/unapplied_changes_probe.py deleted file mode 100755 index 6065f3df28..0000000000 --- a/deploy/probers/unapplied_changes_probe.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python -from base import BaseProbe - - -class UnappliedChangesProbe(BaseProbe): - - metric = "unapplied__changes_ping_latency_msec" - - def do_probe(self): - r = self.request("api/probers/unapplied_changes_status/") - r.raise_for_status() - results = r.json() - - active_task_count = results.get("active_task_count", 0) - unapplied_changes_count = results.get("unapplied_changes_count", 0) - - if active_task_count == 0 and unapplied_changes_count > 0: - raise Exception( - "There are unapplied changes and no active tasks! {} unapplied changes".format( - unapplied_changes_count - ) - ) - - -if __name__ == "__main__": - UnappliedChangesProbe().run() diff --git a/deploy/probers/worker_probe.py b/deploy/probers/worker_probe.py deleted file mode 100755 index 211dc2e6a1..0000000000 --- a/deploy/probers/worker_probe.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python -from base import BaseProbe - - -class WorkerProbe(BaseProbe): - - metric = "worker_ping_latency_msec" - - def do_probe(self): - r = self.request("api/probers/celery_worker_status/") - r.raise_for_status() - results = r.json() - - active_workers = [] - for worker_hostname, worker_status in results.items(): - if "ok" in worker_status.keys(): - active_workers.append(worker_hostname) - - if not active_workers: - raise Exception("No workers are running!") - - -if __name__ == "__main__": - WorkerProbe().run() diff --git a/docker-compose.yml b/docker-compose.yml index 3a07894c8d..68bb5e2500 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -35,7 +35,7 @@ services: platform: linux/amd64 build: context: . - dockerfile: k8s/images/nginx/Dockerfile + dockerfile: docker/Dockerfile.nginx.prod ports: - "8081:8080" depends_on: @@ -44,7 +44,7 @@ services: studio-app: <<: *studio-worker - entrypoint: python docker/entrypoint.py + entrypoint: python docker/studio-dev/entrypoint.py command: pnpm run devserver ports: - "8080:8080" @@ -83,17 +83,6 @@ services: redis: image: redis:6.0.9 - cloudprober: - <<: *studio-worker - working_dir: /src/deploy - entrypoint: "" - # sleep 30 seconds allowing some time for the studio app to start up - command: '/bin/bash -c "sleep 30 && /bin/cloudprober --config_file ./cloudprober.cfg"' - # wait until the main app and celery worker have started - depends_on: - - studio-app - - celery-worker - volumes: minio: diff --git a/docker/Dockerfile.demo b/docker/Dockerfile.demo deleted file mode 100644 index 2ae03758b6..0000000000 --- a/docker/Dockerfile.demo +++ /dev/null @@ -1,43 +0,0 @@ -FROM python:3.10-slim-bookworm - -# Set the timezone -RUN ln -fs /usr/share/zoneinfo/America/Los_Angeles /etc/localtime - -ENV DEBIAN_FRONTEND noninteractive -# Default Python file.open file encoding to UTF-8 instead of ASCII, workaround for le-utils setup.py issue -ENV LANG C.UTF-8 -RUN apt-get update && apt-get -y install python3-pip python3-dev gcc libpq-dev make git curl libjpeg-dev libssl-dev libffi-dev ffmpeg - -# Pin, Download and install node 18.x -RUN apt-get update \ - && apt-get install -y ca-certificates curl gnupg \ - && mkdir -p /etc/apt/keyrings \ - && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ - && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \ - && echo "Package: nodejs" >> /etc/apt/preferences.d/preferences \ - && echo "Pin: origin deb.nodesource.com" >> /etc/apt/preferences.d/preferences \ - && echo "Pin-Priority: 1001" >> /etc/apt/preferences.d/preferences\ - && apt-get update \ - && apt-get install -y nodejs - -RUN corepack enable pnpm -COPY ./package.json . -COPY ./pnpm-lock.yaml . -RUN pnpm install - -COPY requirements.txt . - -RUN pip install --upgrade pip -RUN pip install --ignore-installed -r requirements.txt - -COPY . /contentcuration/ -WORKDIR /contentcuration - -# generate the node bundles -RUN mkdir -p contentcuration/static/js/bundles -RUN ln -s /node_modules /contentcuration/node_modules -RUN pnpm run build - -EXPOSE 8000 - -ENTRYPOINT ["make", "altprodserver"] diff --git a/docker/Dockerfile.nginx.prod b/docker/Dockerfile.nginx.prod new file mode 100644 index 0000000000..ba33210a28 --- /dev/null +++ b/docker/Dockerfile.nginx.prod @@ -0,0 +1,15 @@ +FROM nginx:1.25 + +# Build from inside the directory by overriding this. +ARG SRC_DIR=docker/nginx + +RUN rm /etc/nginx/conf.d/* # if there's stuff here, nginx won't read sites-enabled +COPY ${SRC_DIR}/nginx.conf /etc/nginx/nginx.conf +COPY ${SRC_DIR}/includes /etc/nginx/includes +COPY ${SRC_DIR}/entrypoint.sh /usr/bin + +# Really seems like it _should_ be here, as it's referenced by `nginx.conf`. +# But it's hasn't been for years. +# COPY ${SRC_DIR}/mime.types /etc/nginx/mime.types + +CMD ["entrypoint.sh"] diff --git a/docker/Dockerfile.prod b/docker/Dockerfile.prod deleted file mode 120000 index 11036b6d36..0000000000 --- a/docker/Dockerfile.prod +++ /dev/null @@ -1 +0,0 @@ -../k8s/images/app/Dockerfile \ No newline at end of file diff --git a/docker/Dockerfile.prod b/docker/Dockerfile.prod new file mode 100644 index 0000000000..a9477f90ed --- /dev/null +++ b/docker/Dockerfile.prod @@ -0,0 +1,47 @@ +FROM python:3.10-slim-bookworm + +# Set the timezone +RUN ln -fs /usr/share/zoneinfo/America/Los_Angeles /etc/localtime + +ENV DEBIAN_FRONTEND noninteractive +# Default Python file.open file encoding to UTF-8 instead of ASCII, workaround for le-utils setup.py issue +ENV LANG C.UTF-8 +RUN apt-get update && apt-get -y install python3-pip python3-dev gcc libpq-dev libssl-dev libffi-dev make git curl libjpeg-dev ffmpeg + +# Pin, Download and install node 18.x +RUN apt-get update \ + && apt-get install -y ca-certificates curl gnupg \ + && mkdir -p /etc/apt/keyrings \ + && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ + && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \ + && echo "Package: nodejs" >> /etc/apt/preferences.d/preferences \ + && echo "Pin: origin deb.nodesource.com" >> /etc/apt/preferences.d/preferences \ + && echo "Pin-Priority: 1001" >> /etc/apt/preferences.d/preferences\ + && apt-get update \ + && apt-get install -y nodejs + +RUN corepack enable pnpm + +COPY ./package.json . +COPY ./pnpm-lock.yaml . +RUN pnpm install + +COPY requirements.txt . + +RUN pip install --upgrade pip +RUN pip install --ignore-installed -r requirements.txt + +COPY . /contentcuration/ +WORKDIR /contentcuration + +# generate the node bundles +RUN mkdir -p contentcuration/static/js/bundles +RUN ln -s /node_modules /contentcuration/node_modules +RUN pnpm run build + +ARG COMMIT_SHA +ENV RELEASE_COMMIT_SHA=$COMMIT_SHA + +EXPOSE 8000 + +ENTRYPOINT ["make", "altprodserver"] diff --git a/k8s/images/nginx/entrypoint.sh b/docker/nginx/entrypoint.sh similarity index 100% rename from k8s/images/nginx/entrypoint.sh rename to docker/nginx/entrypoint.sh diff --git a/deploy/includes/README.md b/docker/nginx/includes/README.md similarity index 100% rename from deploy/includes/README.md rename to docker/nginx/includes/README.md diff --git a/deploy/includes/content/_proxy.conf b/docker/nginx/includes/content/_proxy.conf similarity index 100% rename from deploy/includes/content/_proxy.conf rename to docker/nginx/includes/content/_proxy.conf diff --git a/deploy/includes/content/default.conf b/docker/nginx/includes/content/default.conf similarity index 95% rename from deploy/includes/content/default.conf rename to docker/nginx/includes/content/default.conf index 404bd64075..c2c95df613 100644 --- a/deploy/includes/content/default.conf +++ b/docker/nginx/includes/content/default.conf @@ -1,4 +1,4 @@ -# DO NOT RENAME: referenced by k8s/images/nginx/entrypoint.sh +# DO NOT RENAME: referenced by docker/nginx/entrypoint.sh # assume development location @emulator { diff --git a/deploy/includes/content/develop-studio-content.conf b/docker/nginx/includes/content/develop-studio-content.conf similarity index 100% rename from deploy/includes/content/develop-studio-content.conf rename to docker/nginx/includes/content/develop-studio-content.conf diff --git a/deploy/includes/content/studio-content.conf b/docker/nginx/includes/content/studio-content.conf similarity index 100% rename from deploy/includes/content/studio-content.conf rename to docker/nginx/includes/content/studio-content.conf diff --git a/deploy/mime.types b/docker/nginx/mime.types similarity index 100% rename from deploy/mime.types rename to docker/nginx/mime.types diff --git a/deploy/nginx.conf b/docker/nginx/nginx.conf similarity index 100% rename from deploy/nginx.conf rename to docker/nginx/nginx.conf diff --git a/docker/entrypoint.py b/docker/studio-dev/entrypoint.py similarity index 100% rename from docker/entrypoint.py rename to docker/studio-dev/entrypoint.py diff --git a/k8s/Chart.lock b/k8s/Chart.lock deleted file mode 100644 index 3710092dd1..0000000000 --- a/k8s/Chart.lock +++ /dev/null @@ -1,9 +0,0 @@ -dependencies: -- name: cloudsql-proxy - repository: https://storage.googleapis.com/t3n-helm-charts - version: 2.0.0 -- name: redis - repository: https://charts.bitnami.com/bitnami - version: 12.1.1 -digest: sha256:8e1cf67168047aa098bae2eca9ddae32bb68ba68ca80668492760037fe8ce4ef -generated: "2020-11-25T04:47:31.298097908-08:00" diff --git a/k8s/Chart.yaml b/k8s/Chart.yaml deleted file mode 100644 index 0395068cfc..0000000000 --- a/k8s/Chart.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v2 -description: Kolibri Studio, the Content Curation tool for Kolibri! -name: studio -version: 0.3.0 -dependencies: - - name: cloudsql-proxy - version: 2.0.0 - repository: https://storage.googleapis.com/t3n-helm-charts - enabled: true - - name: redis - version: 12.1.1 - repository: https://charts.bitnami.com/bitnami - enabled: true diff --git a/k8s/Makefile b/k8s/Makefile deleted file mode 100644 index c81ba867d9..0000000000 --- a/k8s/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -DEPLOYMENT := `kubectl get deploy -l app=master-studio -o custom-columns=NAME:.metadata.name --no-headers` -POD := `kubectl get pods -o=custom-columns=NAME:.metadata.name --field-selector=status.phase=Running --no-headers -l app=master-studio | head -n 1` - -master-shell: - kubectl rollout status deployment/$(DEPLOYMENT) - echo Running bash inside $(POD) - kubectl exec -it $(POD) bash diff --git a/k8s/charts/cloudsql-proxy-2.0.0.tgz b/k8s/charts/cloudsql-proxy-2.0.0.tgz deleted file mode 100644 index 29a9046b19..0000000000 Binary files a/k8s/charts/cloudsql-proxy-2.0.0.tgz and /dev/null differ diff --git a/k8s/charts/redis-12.1.1.tgz b/k8s/charts/redis-12.1.1.tgz deleted file mode 100644 index b6735330f3..0000000000 Binary files a/k8s/charts/redis-12.1.1.tgz and /dev/null differ diff --git a/k8s/create-cloudsql-database.sh b/k8s/create-cloudsql-database.sh deleted file mode 100755 index f022586047..0000000000 --- a/k8s/create-cloudsql-database.sh +++ /dev/null @@ -1,25 +0,0 @@ -set -e - -DBNAME=$1 - -INSTANCE=$2 - -DATABASES=`gcloud sql databases list --instance=${INSTANCE} | awk '{print $1}' | tail -n +2` - -EXISTENCE=False - -for word in ${DATABASES}; do - if [[ ${word} = ${DBNAME} ]]; - then - echo "Database ${DBNAME} exists in SQL instance ${INSTANCE}." - EXISTENCE=True - break - fi -done - - -if [[ ${EXISTENCE} = False ]]; -then - echo "Creating database ${DBNAME} in SQL instance ${INSTANCE}." - gcloud sql databases create ${DBNAME} --instance=${INSTANCE} -fi diff --git a/k8s/create-cloudsql-proxy.sh b/k8s/create-cloudsql-proxy.sh deleted file mode 100755 index e9adfd0c8b..0000000000 --- a/k8s/create-cloudsql-proxy.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env sh -# Arguments: -# $1: The Helm release name. You'll see this name inside Kubernetes. -# $2: the path to the service account JSON file that has access to Cloud SQL. -# $3: Cloud SQL instance name -# $4: GCP project id -# $5: Cloud SQL region - -set -xe - -# Install the helm server side component -helm init - -# Install the Global cluster sql proxy. Create one for each -# Cloud SQL database you want to connect to. -helm upgrade $1 stable/gcloud-sqlproxy --namespace sqlproxy \ - --set serviceAccountKey="$(cat $2 | base64)" \ - --set cloudsql.instances[0].instance=$3 \ - --set cloudsql.instances[0].project=$4 \ - --set cloudsql.instances[0].region=$5 \ - --set cloudsql.instances[0].port=5432 -i diff --git a/k8s/create-multiplexing-reverse-proxy-lb.sh b/k8s/create-multiplexing-reverse-proxy-lb.sh deleted file mode 100755 index 22f6ede359..0000000000 --- a/k8s/create-multiplexing-reverse-proxy-lb.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env sh -# This script creates a Traefik load balancer, a single -# set of servers that we'll use to multiplex between different -# versions of studio. -# -# We want to use this vs. Google Cloud native load balancers, b/c -# each gcloud lb is $20/month. Using an internal traefik load balancer -# means we only need a static IP, and traefik does the load balancing. - -# Arguments: -# $1: The Helm release name. You'll see this name inside Kubernetes. -# $2: the load balancer's external IP (xxx.xxx.xxx.xxx). Make sure to reserve this first on GCP. -# $3: The Cloudflare email address. Used to perform automated letsencrypt verification. -# $4: The API key for the given Cloudflare email. - - -set -xe - -helm init - -helm upgrade $1 stable/traefik --namespace kube-system \ - --set loadBalancerIP=$2 \ - --set gzip.enabled=false \ - --set acme.enabled=true \ - --set ssl.enabled=true \ - --set acme.challengeType=dns-01 \ - --set acme.dnsProvider.name=cloudflare \ - --set acme.dnsProvider.cloudflare.CLOUDFLARE_EMAIL=$3 \ - --set acme.dnsProvider.cloudflare.CLOUDFLARE_API_KEY=$4 \ - --set acme.email='admins@learningequality.org' \ - --set cpuRequest=1000m \ - --set memoryRequest=1Gi \ - --set cpuLimit=2000m \ - --set memoryLimit=2Gi \ - --set acme.staging=false \ - --set dashboard.enabled=true \ - --set dashboard.domain=traefik-lb-ui.cd.learningequality.org \ - -i diff --git a/k8s/create-postgres-user-and-db.exp b/k8s/create-postgres-user-and-db.exp deleted file mode 100755 index af22daf2d2..0000000000 --- a/k8s/create-postgres-user-and-db.exp +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/local/bin/expect -f -# -# This Expect script was generated by autoexpect on Tue Jun 26 17:27:40 2018 -# Expect and autoexpect were both written by Don Libes, NIST. -# -# Note that autoexpect does not guarantee a working script. It -# necessarily has to guess about certain things. Two reasons a script -# might fail are: -# -# 1) timing - A surprising number of programs (rn, ksh, zsh, telnet, -# etc.) and devices discard or ignore keystrokes that arrive "too -# quickly" after prompts. If you find your new script hanging up at -# one spot, try adding a short sleep just before the previous send. -# Setting "force_conservative" to 1 (see below) makes Expect do this -# automatically - pausing briefly before sending each character. This -# pacifies every program I know of. The -c flag makes the script do -# this in the first place. The -C flag allows you to define a -# character to toggle this mode off and on. - -set force_conservative 0 ;# set to 1 to force conservative mode even if - ;# script wasn't run conservatively originally -if {$force_conservative} { - set send_slow {1 .1} - proc send {ignore arg} { - sleep .1 - exp_send -s -- $arg - } -} - -# -# 2) differing output - Some programs produce different output each time -# they run. The "date" command is an obvious example. Another is -# ftp, if it produces throughput statistics at the end of a file -# transfer. If this causes a problem, delete these patterns or replace -# them with wildcards. An alternative is to use the -p flag (for -# "prompt") which makes Expect only look for the last line of output -# (i.e., the prompt). The -P flag allows you to define a character to -# toggle this mode off and on. -# -# Read the man page for more info. -# -# -Don - -# Aron: call this script with the following arguments -# $1: the name of both the user and the database they control -# $2: the postgres user's password. Needed to create the user. -set user [lindex $argv 0] -set db [lindex $argv 0] -set postgres_password [lindex $argv 1] - - -set timeout -1 -spawn gcloud sql connect studio-qa --project ops-central -match_max 100000 -expect -exact "Password for user postgres: " -send -- "$postgres_password\n" -expect -exact "postgres=> " -send -- "CREATE DATABASE $db; CREATE USER $user WITH ENCRYPTED PASSWORD '$user'; GRANT ALL PRIVILEGES ON DATABASE $db TO $user;" -send -- "\n" -expect -exact "postgres=>" -send -- "" -expect eof diff --git a/k8s/encrypt-env-var.sh b/k8s/encrypt-env-var.sh deleted file mode 100755 index c20e0774b0..0000000000 --- a/k8s/encrypt-env-var.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -# How to use: -# pipe a secret string into this script. -# This will output instructions on what you -# then need to add into your cloudbuild.yaml file. - -KEYRING=$1 -KEY=$2 - -gcloud kms encrypt \ - --plaintext-file=- \ - --ciphertext-file=- \ - --location=global \ - --keyring=$KEYRING \ - --key=$KEY \ - | base64 diff --git a/k8s/helm-deploy.sh b/k8s/helm-deploy.sh deleted file mode 100755 index 4f512559fd..0000000000 --- a/k8s/helm-deploy.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -RELEASE_NAME=$1 -STUDIO_APP_IMAGE_NAME=$2 -STUDIO_NGINX_IMAGE_NAME=$3 -STUDIO_BUCKET_NAME=$4 -COMMIT_SHA=$5 -PROJECT_ID=$6 -DATABASE_INSTANCE_NAME=$7 -DATABASE_REGION=$8 - -K8S_DIR=$(dirname $0) - -function get_secret { - gcloud secrets versions access --secret=$1 latest -} - -helm upgrade --install \ - --namespace $RELEASE_NAME --create-namespace \ - --set studioApp.postmarkApiKey=$(get_secret postmark-api-key) \ - --set studioApp.releaseCommit=$COMMIT_SHA \ - --set studioApp.imageName=$STUDIO_APP_IMAGE_NAME \ - --set studioNginx.imageName=$STUDIO_NGINX_IMAGE_NAME \ - --set studioApp.gcs.bucketName=$STUDIO_BUCKET_NAME \ - --set studioApp.gcs.writerServiceAccountKeyBase64Encoded=$(get_secret studio-gcs-service-account-key | base64 -w 0) \ - --set settings=contentcuration.production_settings \ - --set sentry.dsnKey=$(get_secret sentry-dsn-key) \ - --set redis.password=$(get_secret redis-password) \ - --set cloudsql-proxy.credentials.username=$(get_secret postgres-username) \ - --set cloudsql-proxy.credentials.password=$(get_secret postgres-password) \ - --set cloudsql-proxy.credentials.dbname=$(get_secret postgres-dbname) \ - --set cloudsql-proxy.cloudsql.instances[0].instance=$DATABASE_INSTANCE_NAME \ - --set cloudsql-proxy.cloudsql.instances[0].project=$PROJECT_ID \ - --set cloudsql-proxy.cloudsql.instances[0].region=$DATABASE_REGION \ - --set cloudsql-proxy.cloudsql.instances[0].port=5432 \ - $RELEASE_NAME $K8S_DIR diff --git a/k8s/images/app/Dockerfile b/k8s/images/app/Dockerfile deleted file mode 100644 index a9477f90ed..0000000000 --- a/k8s/images/app/Dockerfile +++ /dev/null @@ -1,47 +0,0 @@ -FROM python:3.10-slim-bookworm - -# Set the timezone -RUN ln -fs /usr/share/zoneinfo/America/Los_Angeles /etc/localtime - -ENV DEBIAN_FRONTEND noninteractive -# Default Python file.open file encoding to UTF-8 instead of ASCII, workaround for le-utils setup.py issue -ENV LANG C.UTF-8 -RUN apt-get update && apt-get -y install python3-pip python3-dev gcc libpq-dev libssl-dev libffi-dev make git curl libjpeg-dev ffmpeg - -# Pin, Download and install node 18.x -RUN apt-get update \ - && apt-get install -y ca-certificates curl gnupg \ - && mkdir -p /etc/apt/keyrings \ - && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ - && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \ - && echo "Package: nodejs" >> /etc/apt/preferences.d/preferences \ - && echo "Pin: origin deb.nodesource.com" >> /etc/apt/preferences.d/preferences \ - && echo "Pin-Priority: 1001" >> /etc/apt/preferences.d/preferences\ - && apt-get update \ - && apt-get install -y nodejs - -RUN corepack enable pnpm - -COPY ./package.json . -COPY ./pnpm-lock.yaml . -RUN pnpm install - -COPY requirements.txt . - -RUN pip install --upgrade pip -RUN pip install --ignore-installed -r requirements.txt - -COPY . /contentcuration/ -WORKDIR /contentcuration - -# generate the node bundles -RUN mkdir -p contentcuration/static/js/bundles -RUN ln -s /node_modules /contentcuration/node_modules -RUN pnpm run build - -ARG COMMIT_SHA -ENV RELEASE_COMMIT_SHA=$COMMIT_SHA - -EXPOSE 8000 - -ENTRYPOINT ["make", "altprodserver"] diff --git a/k8s/images/app/Dockerfile b/k8s/images/app/Dockerfile new file mode 120000 index 0000000000..4750df1fc3 --- /dev/null +++ b/k8s/images/app/Dockerfile @@ -0,0 +1 @@ +docker/Dockerfile.prod \ No newline at end of file diff --git a/k8s/images/app/Makefile b/k8s/images/app/Makefile deleted file mode 100644 index d958cc266a..0000000000 --- a/k8s/images/app/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -COMMIT := nlatest - -imagebuild: - docker build ../../../ -f $$PWD/Dockerfile -t gcr.io/github-learningequality-studio/app:$(COMMIT) diff --git a/k8s/images/nginx/Dockerfile b/k8s/images/nginx/Dockerfile deleted file mode 100644 index ab38a1118a..0000000000 --- a/k8s/images/nginx/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM nginx:1.25 - -RUN rm /etc/nginx/conf.d/* # if there's stuff here, nginx won't read sites-enabled -COPY deploy/nginx.conf /etc/nginx/nginx.conf -COPY deploy/includes /etc/nginx/includes -COPY k8s/images/nginx/entrypoint.sh /usr/bin - -CMD ["entrypoint.sh"] diff --git a/k8s/images/nginx/Dockerfile b/k8s/images/nginx/Dockerfile new file mode 120000 index 0000000000..a4867c19b0 --- /dev/null +++ b/k8s/images/nginx/Dockerfile @@ -0,0 +1 @@ +docker/Dockerfile.nginx.prod \ No newline at end of file diff --git a/k8s/images/nginx/Makefile b/k8s/images/nginx/Makefile deleted file mode 100644 index 9c2d01dc60..0000000000 --- a/k8s/images/nginx/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -CONTAINER_NAME := "contentworkshop-app-nginx-proxy" -CONTAINER_VERSION := v4 -GCLOUD_PROJECT := contentworkshop-159920 -GIT_PROJECT_ROOT := `git rev-parse --show-toplevel` - -all: appcodeupdate imagebuild imagepush - -imagebuild: - docker build -t learningequality/$(CONTAINER_NAME):$(CONTAINER_VERSION) -f ./Dockerfile $(GIT_PROJECT_ROOT) diff --git a/k8s/images/prober/Dockerfile b/k8s/images/prober/Dockerfile deleted file mode 100644 index d3d18ee8a6..0000000000 --- a/k8s/images/prober/Dockerfile +++ /dev/null @@ -1,9 +0,0 @@ -FROM ubuntu:bionic - -RUN apt-get update && apt-get install -y curl python-pip unzip - -RUN pip install requests>=2.20.0 && pip install psycopg2-binary==2.7.4 && pip install le-utils>=0.1.19 - -COPY ./deploy/cloudprober.cfg /deploy/ -COPY ./deploy/prober-entrypoint.sh /deploy/ -COPY ./deploy/probers /deploy/probers/ diff --git a/k8s/templates/_helpers.tpl b/k8s/templates/_helpers.tpl deleted file mode 100644 index 1098c07dc7..0000000000 --- a/k8s/templates/_helpers.tpl +++ /dev/null @@ -1,134 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "studio.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "studio.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{- define "cloudsql-proxy.fullname" -}} -{{- $name := .Release.Name -}} -{{- printf "%s-%s" $name "cloudsql-proxy" | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- define "redis.fullname" -}} -{{- $name := .Release.Name -}} -{{- printf "%s-%s" $name "redis" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{- define "minio.url" -}} -{{- printf "http://%s-%s:%v" .Release.Name "minio" .Values.minio.service.port -}} -{{- end -}} - - -{{/* -Return the appropriate apiVersion for networkpolicy. -*/}} -{{- define "studio.networkPolicy.apiVersion" -}} -{{- if semverCompare ">=1.4-0, <1.7-0" .Capabilities.KubeVersion.GitVersion -}} -"extensions/v1" -{{- else if semverCompare "^1.7-0" .Capabilities.KubeVersion.GitVersion -}} -"networking.k8s.io/v1" -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "studio.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Generate chart secret name -*/}} -{{- define "studio.secretName" -}} -{{ default (include "studio.fullname" .) .Values.existingSecret }} -{{- end -}} - -{{/* -Generate the shared environment variables between studio app and workers -*/}} -{{- define "studio.sharedEnvs" -}} -- name: DJANGO_SETTINGS_MODULE - value: {{ .Values.settings }} -- name: DJANGO_LOG_FILE - value: /var/log/django.log -- name: MPLBACKEND - value: PS -- name: STUDIO_BETA_MODE - value: "yes" -- name: RUN_MODE - value: k8s -- name: DATA_DB_NAME - valueFrom: - secretKeyRef: - key: postgres-database - name: {{ template "studio.fullname" . }} -- name: DATA_DB_PORT - value: "5432" -- name: DATA_DB_USER - valueFrom: - secretKeyRef: - key: postgres-user - name: {{ template "studio.fullname" . }} -- name: DATA_DB_PASS - valueFrom: - secretKeyRef: - key: postgres-password - name: {{ template "studio.fullname" . }} -- name: CELERY_TIMEZONE - value: America/Los_Angeles -- name: CELERY_REDIS_DB - value: "0" -- name: CELERY_BROKER_ENDPOINT - value: {{ template "redis.fullname" . }}-master -- name: CELERY_RESULT_BACKEND_ENDPOINT - value: {{ template "redis.fullname" . }}-master -- name: CELERY_REDIS_PASSWORD - valueFrom: - secretKeyRef: - key: redis-password - name: {{ template "studio.fullname" . }} -- name: AWS_S3_ENDPOINT_URL - value: https://storage.googleapis.com -- name: RELEASE_COMMIT_SHA - value: {{ .Values.studioApp.releaseCommit | default "" }} -- name: BRANCH_ENVIRONMENT - value: {{ .Release.Name }} -- name: SENTRY_DSN_KEY - valueFrom: - secretKeyRef: - key: sentry-dsn-key - name: {{ template "studio.fullname" . }} - optional: true -- name: AWS_BUCKET_NAME - value: {{ .Values.studioApp.gcs.bucketName }} -- name: EMAIL_CREDENTIALS_POSTMARK_API_KEY - {{ if .Values.studioApp.postmarkApiKey }} - valueFrom: - secretKeyRef: - key: postmark-api-key - name: {{ template "studio.fullname" . }} - {{ else }} - value: "" - {{ end }} - -{{- end -}} diff --git a/k8s/templates/garbage-collect-cronjob.yaml b/k8s/templates/garbage-collect-cronjob.yaml deleted file mode 100644 index 4395732541..0000000000 --- a/k8s/templates/garbage-collect-cronjob.yaml +++ /dev/null @@ -1,78 +0,0 @@ ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ template "studio.fullname" . }}-garbage-collect-job-config - labels: - tier: job - app: {{ template "studio.fullname" . }} - chart: {{ .Chart.Name }} - release: {{ .Release.Name }} -data: - DJANGO_LOG_FILE: /var/log/django.log - DATA_DB_HOST: {{ template "cloudsql-proxy.fullname" . }} - DATA_DB_PORT: "5432" - MPLBACKEND: PS - RUN_MODE: k8s - RELEASE_COMMIT_SHA: {{ .Values.studioApp.releaseCommit | default "" }} - BRANCH_ENVIRONMENT: {{ .Release.Name }} - AWS_BUCKET_NAME: {{ .Values.studioApp.gcs.bucketName }} ---- -apiVersion: v1 -kind: Secret -metadata: - name: {{ template "studio.fullname" . }}-garbage-collect-job-secret - labels: - app: {{ template "studio.fullname" . }} - chart: {{ .Chart.Name }} - release: {{ .Release.Name }} -type: Opaque -data: - DATA_DB_USER: {{ index .Values "cloudsql-proxy" "credentials" "username" | b64enc }} - DATA_DB_PASS: {{ index .Values "cloudsql-proxy" "credentials" "password" | b64enc }} - DATA_DB_NAME: {{ index .Values "cloudsql-proxy" "credentials" "dbname" | b64enc }} - SENTRY_DSN_KEY: {{ .Values.sentry.dsnKey | b64enc }} ---- -apiVersion: batch/v1beta1 -kind: CronJob -metadata: - name: {{ template "studio.fullname" . }}-garbage-collect-cronjob - labels: - tier: job - chart: {{ .Chart.Name }} - release: {{ .Release.Name }} -spec: - schedule: "@midnight" - jobTemplate: - spec: - template: - spec: - restartPolicy: OnFailure - containers: - - name: app - image: {{ .Values.studioApp.imageName }} - command: - - python - - contentcuration/manage.py - - garbage_collect - env: - - name: DJANGO_SETTINGS_MODULE - value: contentcuration.production_settings - envFrom: - - configMapRef: - name: {{ template "studio.fullname" . }}-garbage-collect-job-config - - secretRef: - name: {{ template "studio.fullname" . }}-garbage-collect-job-secret - resources: - requests: - cpu: 0.5 - memory: 1Gi - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: full-gcp-access-scope - operator: In - values: - - "true" diff --git a/k8s/templates/ingress.yaml b/k8s/templates/ingress.yaml deleted file mode 100644 index c2e199dc7a..0000000000 --- a/k8s/templates/ingress.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -apiVersion: networking.k8s.io/v1beta1 -kind: Ingress -metadata: - name: {{ template "studio.fullname" . }} - labels: - app: {{ template "studio.fullname" . }} - tier: ingress - annotations: - ingress.kubernetes.io/rewrite-target: / - kubernetes.io/ingress.class: "nginx" - ingressClassName: "nginx" - -spec: - rules: - - host: {{.Release.Name}}.studio.cd.learningequality.org - http: - paths: - - backend: - serviceName: {{ template "studio.fullname" . }}-app - servicePort: 80 diff --git a/k8s/templates/job-template.yaml b/k8s/templates/job-template.yaml deleted file mode 100644 index 856f27371c..0000000000 --- a/k8s/templates/job-template.yaml +++ /dev/null @@ -1,73 +0,0 @@ ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ template "studio.fullname" . }}-db-migrate-config - labels: - app: {{ template "studio.fullname" . }} - annotations: - "helm.sh/hook": pre-install,pre-upgrade - "helm.sh/hook-delete-policy": before-hook-creation -data: - DJANGO_SETTINGS_MODULE: {{ .Values.settings }} - DJANGO_LOG_FILE: /var/log/django.log - DATA_DB_HOST: {{ template "cloudsql-proxy.fullname" . }} - DATA_DB_PORT: "5432" - MPLBACKEND: PS - STUDIO_BETA_MODE: "yes" - RUN_MODE: k8s - RELEASE_COMMIT_SHA: {{ .Values.studioApp.releaseCommit | default "" }} - BRANCH_ENVIRONMENT: {{ .Release.Name }} ---- -apiVersion: v1 -kind: Secret -metadata: - name: {{ template "studio.fullname" . }}-db-migrate-secrets - labels: - app: studio - chart: {{ .Chart.Name }} - release: {{ .Release.Name }} - annotations: - "helm.sh/hook": pre-install,pre-upgrade - "helm.sh/hook-delete-policy": before-hook-creation -type: Opaque -data: - DATA_DB_USER: {{ index .Values "cloudsql-proxy" "credentials" "username" | b64enc }} - DATA_DB_PASS: {{ index .Values "cloudsql-proxy" "credentials" "password" | b64enc }} - DATA_DB_NAME: {{ index .Values "cloudsql-proxy" "credentials" "dbname" | b64enc }} - SENTRY_DSN_KEY: {{ .Values.sentry.dsnKey | b64enc }} ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: {{ template "studio.fullname" . }}-migrate-job - labels: - app: {{ template "studio.fullname" . }} - annotations: - "helm.sh/hook": post-install,pre-upgrade - "helm.sh/hook-delete-policy": before-hook-creation -spec: - template: - spec: - restartPolicy: OnFailure - containers: - - name: dbmigrate - image: {{ .Values.studioApp.imageName }} - command: - - make - - migrate - envFrom: - - configMapRef: - name: {{ template "studio.fullname" . }}-db-migrate-config - - secretRef: - name: {{ template "studio.fullname" . }}-db-migrate-secrets - env: - - name: DJANGO_SETTINGS_MODULE - value: contentcuration.migration_production_settings - resources: - requests: - cpu: 1 - memory: 2Gi - limits: - cpu: 1 - memory: 2Gi diff --git a/k8s/templates/mark-incomplete-mgmt-command-cronjob.yaml b/k8s/templates/mark-incomplete-mgmt-command-cronjob.yaml deleted file mode 100644 index ad36b8b0e4..0000000000 --- a/k8s/templates/mark-incomplete-mgmt-command-cronjob.yaml +++ /dev/null @@ -1,78 +0,0 @@ ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ template "studio.fullname" . }}-mark-incomplete-job-config - labels: - tier: job - app: {{ template "studio.fullname" . }} - chart: {{ .Chart.Name }} - release: {{ .Release.Name }} -data: - DJANGO_LOG_FILE: /var/log/django.log - DATA_DB_HOST: {{ template "cloudsql-proxy.fullname" . }} - DATA_DB_PORT: "5432" - MPLBACKEND: PS - RUN_MODE: k8s - RELEASE_COMMIT_SHA: {{ .Values.studioApp.releaseCommit | default "" }} - BRANCH_ENVIRONMENT: {{ .Release.Name }} - AWS_BUCKET_NAME: {{ .Values.studioApp.gcs.bucketName }} ---- -apiVersion: v1 -kind: Secret -metadata: - name: {{ template "studio.fullname" . }}-mark-incomplete-job-secrets - labels: - app: {{ template "studio.fullname" . }} - chart: {{ .Chart.Name }} - release: {{ .Release.Name }} -type: Opaque -data: - DATA_DB_USER: {{ index .Values "cloudsql-proxy" "credentials" "username" | b64enc }} - DATA_DB_PASS: {{ index .Values "cloudsql-proxy" "credentials" "password" | b64enc }} - DATA_DB_NAME: {{ index .Values "cloudsql-proxy" "credentials" "dbname" | b64enc }} - SENTRY_DSN_KEY: {{ .Values.sentry.dsnKey | b64enc }} ---- -apiVersion: batch/v1beta1 -kind: CronJob -metadata: - name: mark-incomplete-cronjob - labels: - tier: job - chart: {{ .Chart.Name }} - release: {{ .Release.Name }} -spec: - schedule: "00 12 10 */36 *" - jobTemplate: - spec: - template: - spec: - restartPolicy: OnFailure - containers: - - name: app - image: {{ .Values.studioApp.imageName }} - command: - - python - - contentcuration/manage.py - - mark_incomplete - env: - - name: DJANGO_SETTINGS_MODULE - value: contentcuration.production_settings - envFrom: - - configMapRef: - name: {{ template "studio.fullname" . }}-mark-incomplete-job-config - - secretRef: - name: {{ template "studio.fullname" . }}-mark-incomplete-job-secrets - resources: - requests: - cpu: 0.5 - memory: 1Gi - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: full-gcp-access-scope - operator: In - values: - - "true" diff --git a/k8s/templates/production-ingress.yaml b/k8s/templates/production-ingress.yaml deleted file mode 100644 index 68f10481ba..0000000000 --- a/k8s/templates/production-ingress.yaml +++ /dev/null @@ -1,23 +0,0 @@ -{{- if .Values.productionIngress -}} ---- -apiVersion: networking.k8s.io/v1beta1 -kind: Ingress -metadata: - name: {{ template "studio.fullname" . }}-production - labels: - app: {{ template "studio.fullname" . }} - tier: ingress - type: production - annotations: - ingress.kubernetes.io/rewrite-target: / - kubernetes.io/ingress.class: "nginx" - ingressClassName: "nginx" -spec: - rules: - - host: {{.Release.Name}}.studio.learningequality.org - http: - paths: - - backend: - serviceName: {{ template "studio.fullname" . }}-app - servicePort: 80 -{{- end }} diff --git a/k8s/templates/set-storage-used-mgmt-command-cronjob.yaml b/k8s/templates/set-storage-used-mgmt-command-cronjob.yaml deleted file mode 100644 index cd30ba6f2f..0000000000 --- a/k8s/templates/set-storage-used-mgmt-command-cronjob.yaml +++ /dev/null @@ -1,78 +0,0 @@ ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ template "studio.fullname" . }}-set-storage-used-job-config - labels: - tier: job - app: {{ template "studio.fullname" . }} - chart: {{ .Chart.Name }} - release: {{ .Release.Name }} -data: - DJANGO_LOG_FILE: /var/log/django.log - DATA_DB_HOST: {{ template "cloudsql-proxy.fullname" . }} - DATA_DB_PORT: "5432" - MPLBACKEND: PS - RUN_MODE: k8s - RELEASE_COMMIT_SHA: {{ .Values.studioApp.releaseCommit | default "" }} - BRANCH_ENVIRONMENT: {{ .Release.Name }} - AWS_BUCKET_NAME: {{ .Values.studioApp.gcs.bucketName }} ---- -apiVersion: v1 -kind: Secret -metadata: - name: {{ template "studio.fullname" . }}-set-storage-used-job-secrets - labels: - app: {{ template "studio.fullname" . }} - chart: {{ .Chart.Name }} - release: {{ .Release.Name }} -type: Opaque -data: - DATA_DB_USER: {{ index .Values "cloudsql-proxy" "credentials" "username" | b64enc }} - DATA_DB_PASS: {{ index .Values "cloudsql-proxy" "credentials" "password" | b64enc }} - DATA_DB_NAME: {{ index .Values "cloudsql-proxy" "credentials" "dbname" | b64enc }} - SENTRY_DSN_KEY: {{ .Values.sentry.dsnKey | b64enc }} ---- -apiVersion: batch/v1beta1 -kind: CronJob -metadata: - name: set-storage-used-cronjob - labels: - tier: job - chart: {{ .Chart.Name }} - release: {{ .Release.Name }} -spec: - schedule: "@midnight" - jobTemplate: - spec: - template: - spec: - restartPolicy: OnFailure - containers: - - name: app - image: {{ .Values.studioApp.imageName }} - command: - - python - - contentcuration/manage.py - - set_storage_used - env: - - name: DJANGO_SETTINGS_MODULE - value: contentcuration.production_settings - envFrom: - - configMapRef: - name: {{ template "studio.fullname" . }}-set-storage-used-job-config - - secretRef: - name: {{ template "studio.fullname" . }}-set-storage-used-job-secrets - resources: - requests: - cpu: 0.5 - memory: 1Gi - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: full-gcp-access-scope - operator: In - values: - - "true" diff --git a/k8s/templates/studio-deployment.yaml b/k8s/templates/studio-deployment.yaml deleted file mode 100644 index f6a74f36fa..0000000000 --- a/k8s/templates/studio-deployment.yaml +++ /dev/null @@ -1,157 +0,0 @@ ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ template "studio.fullname" . }} - labels: - tier: app - app: {{ template "studio.fullname" . }} -spec: - replicas: {{ .Values.studioApp.replicas }} - selector: - matchLabels: - app: {{ template "studio.fullname" . }} - tier: frontend - template: - metadata: - annotations: - checksum: {{ include (print $.Template.BasePath "/job-template.yaml") . | sha256sum }} - labels: - app: {{ template "studio.fullname" . }} - tier: frontend - spec: - initContainers: - - name: collectstatic - image: {{ .Values.studioApp.imageName }} - workingDir: /contentcuration/ - command: - - make - args: - - collectstatic - env: - - name: DJANGO_SETTINGS_MODULE - value: contentcuration.collectstatic_settings - - name: STATICFILES_DIR - value: /app/contentworkshop_static/ - volumeMounts: - - mountPath: /app/contentworkshop_static/ - name: staticfiles - containers: - - name: app - image: {{ .Values.studioApp.imageName }} - workingDir: /contentcuration/contentcuration/ - command: - - gunicorn - args: - - contentcuration.wsgi:application - - --timeout=4000 - - --workers=2 - - --bind=0.0.0.0:{{ .Values.studioApp.appPort }} - - --pid=/tmp/contentcuration.pid - env: {{ include "studio.sharedEnvs" . | nindent 8 }} - - name: SEND_USER_ACTIVATION_NOTIFICATION_EMAIL - value: "true" - - name: DATA_DB_HOST - value: {{ template "cloudsql-proxy.fullname" . }} - - name: GOOGLE_CLOUD_STORAGE_SERVICE_ACCOUNT_CREDENTIALS - value: /var/secrets/gcs-writer-service-account-key.json - ports: - - containerPort: {{ .Values.studioApp.appPort }} - readinessProbe: - httpGet: - path: /healthz - port: {{ .Values.studioApp.appPort }} - initialDelaySeconds: 5 - periodSeconds: 2 - failureThreshold: 3 - resources: - requests: - cpu: 0.5 - memory: 2Gi - limits: - memory: 2Gi - volumeMounts: - - mountPath: /var/secrets - name: gcs-writer-service-account-key - readOnly: true - - name: nginx-proxy - image: {{ .Values.studioNginx.imageName }} - env: - - name: AWS_S3_ENDPOINT_URL - value: https://storage.googleapis.com - - name: AWS_BUCKET_NAME - value: {{ .Values.studioApp.gcs.bucketName }} - ports: - - containerPort: {{ .Values.studioNginx.port }} - volumeMounts: - - mountPath: /app/contentworkshop_static/ - name: staticfiles - resources: - requests: - cpu: 0.2 - memory: 256Mi - limits: - memory: 512Mi - volumes: - - emptyDir: {} - name: staticfiles - - name: gcs-writer-service-account-key - secret: - secretName: {{ template "studio.fullname" . }} - items: - - key: gcs-writer-service-account-key - path: gcs-writer-service-account-key.json - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: full-gcp-access-scope - operator: In - values: - - "true" ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{template "studio.fullname" . }}-workers -spec: - replicas: {{ .Values.studioWorkers.replicas }} - selector: - matchLabels: - app: {{ template "studio.fullname" . }}-workers - tier: workers - template: - metadata: - labels: - app: {{ template "studio.fullname" . }}-workers - tier: workers - spec: - containers: - - name: worker - image: {{ .Values.studioApp.imageName }} - command: - - make - {{- if not .Values.productionIngress }} - - setup - {{- end }} - - prodceleryworkers - env: {{ include "studio.sharedEnvs" . | nindent 8 }} - - name: DATA_DB_HOST - value: {{ template "cloudsql-proxy.fullname" . }} - resources: - requests: - cpu: 0.5 - memory: 2Gi - limits: - cpu: 2 - memory: 8Gi - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: full-gcp-access-scope - operator: In - values: - - "true" diff --git a/k8s/templates/studio-secrets.yaml b/k8s/templates/studio-secrets.yaml deleted file mode 100644 index 51f2589a3e..0000000000 --- a/k8s/templates/studio-secrets.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: {{ template "studio.fullname" . }} - labels: - app: studio - chart: {{ .Chart.Name }} - release: {{ .Release.Name }} -type: Opaque -data: - postmark-api-key: {{ .Values.studioApp.postmarkApiKey | default "" | b64enc }} - redis-password: {{ .Values.redis.password | default "" | b64enc }} - postgres-user: {{ index .Values "cloudsql-proxy" "credentials" "username" | b64enc }} - postgres-password: {{ index .Values "cloudsql-proxy" "credentials" "password" | b64enc }} - postgres-database: {{ index .Values "cloudsql-proxy" "credentials" "dbname" | b64enc }} - sentry-dsn-key: {{ .Values.sentry.dsnKey | b64enc }} - gcs-writer-service-account-key: {{ .Values.studioApp.gcs.writerServiceAccountKeyBase64Encoded }} diff --git a/k8s/templates/studio-service.yaml b/k8s/templates/studio-service.yaml deleted file mode 100644 index 8f70e6f54d..0000000000 --- a/k8s/templates/studio-service.yaml +++ /dev/null @@ -1,13 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: {{ template "studio.fullname" . }}-app -spec: - ports: - - port: 80 - targetPort: {{ .Values.studioNginx.port }} - selector: - app: {{ template "studio.fullname" . }} - tier: frontend - type: NodePort diff --git a/k8s/values.yaml b/k8s/values.yaml deleted file mode 100644 index 11db6c1559..0000000000 --- a/k8s/values.yaml +++ /dev/null @@ -1,70 +0,0 @@ ---- -# A set of values that are meant to be used for a production setup. -# This includes: -# - an external Postgres, GCS Storage, and external Redis -# - real email sending -# - studio production settings -# -# Note that the secrets will have to be filled up by the caller -# through helm upgrade --set. See REPLACEME placeholders -# for values that need to be set. - -settings: contentcuration.sandbox_settings - -productionIngress: true - -studioApp: - imageName: "REPLACEME" - postmarkApiKey: "REPLACEME" - releaseCommit: "" - replicas: 5 - appPort: 8081 - gcs: - bucketName: develop-studio-content - writerServiceAccountKeyBase64Encoded: "REPLACEME" - pgbouncer: - replicas: 3 - pool_size: 10 - reserve_pool_size: 10 - -studioNginx: - imageName: "REPLACEME" - port: 8080 - -sentry: - dsnKey: "" - -cloudsql-proxy: - enabled: true - cloudsql: - instances: - - instance: "REPLACEME" - project: "REPLACEME" - region: "REPLACEME" - port: 5432 - credentials: - username: "" - password: "" - dbname: "" - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: full-gcp-access-scope - operator: In - values: - - "true" - -redis: - enabled: true - -studioWorkers: - replicas: 5 - - -studioProber: - imageName: "REPLACEME" - loginProberUsername: "REPLACEME" - loginProberPassword: "REPLACEME" - port: 9313