From 05157a633f7cdc99f542951cf5b08c6cafc986c2 Mon Sep 17 00:00:00 2001 From: henrrypg Date: Tue, 4 Jul 2023 16:19:30 -0500 Subject: [PATCH] feat: add support to palm version --- .../infrastructure/tutor_based_manifests.py | 8 +- drydock/references/tutor_v16.yml | 10 + .../tutor16/base/apps/caddy/Caddyfile | 83 ++++ .../base/apps/openedx/config/cms.env.yml | 40 ++ .../base/apps/openedx/config/lms.env.yml | 47 ++ .../apps/openedx/config/partials/auth.yml | 21 + .../apps/openedx/settings/cms/__init__.py | 0 .../apps/openedx/settings/cms/development.py | 20 + .../apps/openedx/settings/cms/production.py | 17 + .../base/apps/openedx/settings/cms/test.py | 3 + .../apps/openedx/settings/lms/__init__.py | 0 .../apps/openedx/settings/lms/development.py | 39 ++ .../apps/openedx/settings/lms/production.py | 33 ++ .../base/apps/openedx/settings/lms/test.py | 3 + .../openedx/settings/partials/common_all.py | 223 +++++++++ .../openedx/settings/partials/common_cms.py | 30 ++ .../openedx/settings/partials/common_lms.py | 52 ++ .../openedx/settings/partials/common_test.py | 3 + .../tutor16/base/apps/redis/redis.conf | 41 ++ .../tutor16/base/k8s/deployments.yml | 467 ++++++++++++++++++ .../kustomized/tutor16/base/k8s/jobs.yml | 88 ++++ .../kustomized/tutor16/base/k8s/namespace.yml | 7 + .../kustomized/tutor16/base/k8s/override.yml | 1 + .../kustomized/tutor16/base/k8s/services.yml | 145 ++++++ .../kustomized/tutor16/base/k8s/volumes.yml | 81 +++ .../kustomized/tutor16/base/kustomization.yml | 70 +++ .../templates/kustomized/tutor16/defaults.yml | 50 ++ .../tutor16/extensions/debug/deployments.yml | 103 ++++ .../tutor16/extensions/debug/ingress.yml | 35 ++ .../tutor16/extensions/debug/services.yml | 30 ++ .../tutor16/extensions/drydock-jobs/cms.yml | 59 +++ .../extensions/drydock-jobs/extra-jobs.yml | 1 + .../tutor16/extensions/drydock-jobs/forum.yml | 46 ++ .../tutor16/extensions/drydock-jobs/lms.yml | 80 +++ .../tutor16/extensions/drydock-jobs/minio.yml | 35 ++ .../tutor16/extensions/drydock-jobs/mysql.yml | 44 ++ .../tutor16/extensions/drydock-jobs/notes.yml | 123 +++++ .../kustomized/tutor16/extensions/flowers.yml | 25 + .../kustomized/tutor16/extensions/hpa.yml | 82 +++ .../kustomized/tutor16/extensions/ingress.yml | 85 ++++ .../tutor16/extensions/kustomization.yml | 52 ++ .../tutor16/extensions/multipurpose-jobs.yml | 1 + .../tutor16/extensions/newrelic.ini | 1 + .../tutor16/extensions/overrides.yml | 132 +++++ .../kustomized/tutor16/kustomization.yml | 17 + 45 files changed, 2529 insertions(+), 4 deletions(-) create mode 100644 drydock/references/tutor_v16.yml create mode 100644 drydock/templates/kustomized/tutor16/base/apps/caddy/Caddyfile create mode 100644 drydock/templates/kustomized/tutor16/base/apps/openedx/config/cms.env.yml create mode 100644 drydock/templates/kustomized/tutor16/base/apps/openedx/config/lms.env.yml create mode 100644 drydock/templates/kustomized/tutor16/base/apps/openedx/config/partials/auth.yml create mode 100644 drydock/templates/kustomized/tutor16/base/apps/openedx/settings/cms/__init__.py create mode 100644 drydock/templates/kustomized/tutor16/base/apps/openedx/settings/cms/development.py create mode 100644 drydock/templates/kustomized/tutor16/base/apps/openedx/settings/cms/production.py create mode 100644 drydock/templates/kustomized/tutor16/base/apps/openedx/settings/cms/test.py create mode 100644 drydock/templates/kustomized/tutor16/base/apps/openedx/settings/lms/__init__.py create mode 100644 drydock/templates/kustomized/tutor16/base/apps/openedx/settings/lms/development.py create mode 100644 drydock/templates/kustomized/tutor16/base/apps/openedx/settings/lms/production.py create mode 100644 drydock/templates/kustomized/tutor16/base/apps/openedx/settings/lms/test.py create mode 100644 drydock/templates/kustomized/tutor16/base/apps/openedx/settings/partials/common_all.py create mode 100644 drydock/templates/kustomized/tutor16/base/apps/openedx/settings/partials/common_cms.py create mode 100644 drydock/templates/kustomized/tutor16/base/apps/openedx/settings/partials/common_lms.py create mode 100644 drydock/templates/kustomized/tutor16/base/apps/openedx/settings/partials/common_test.py create mode 100644 drydock/templates/kustomized/tutor16/base/apps/redis/redis.conf create mode 100644 drydock/templates/kustomized/tutor16/base/k8s/deployments.yml create mode 100644 drydock/templates/kustomized/tutor16/base/k8s/jobs.yml create mode 100644 drydock/templates/kustomized/tutor16/base/k8s/namespace.yml create mode 100644 drydock/templates/kustomized/tutor16/base/k8s/override.yml create mode 100644 drydock/templates/kustomized/tutor16/base/k8s/services.yml create mode 100644 drydock/templates/kustomized/tutor16/base/k8s/volumes.yml create mode 100644 drydock/templates/kustomized/tutor16/base/kustomization.yml create mode 100644 drydock/templates/kustomized/tutor16/defaults.yml create mode 100644 drydock/templates/kustomized/tutor16/extensions/debug/deployments.yml create mode 100644 drydock/templates/kustomized/tutor16/extensions/debug/ingress.yml create mode 100644 drydock/templates/kustomized/tutor16/extensions/debug/services.yml create mode 100644 drydock/templates/kustomized/tutor16/extensions/drydock-jobs/cms.yml create mode 100644 drydock/templates/kustomized/tutor16/extensions/drydock-jobs/extra-jobs.yml create mode 100644 drydock/templates/kustomized/tutor16/extensions/drydock-jobs/forum.yml create mode 100644 drydock/templates/kustomized/tutor16/extensions/drydock-jobs/lms.yml create mode 100644 drydock/templates/kustomized/tutor16/extensions/drydock-jobs/minio.yml create mode 100644 drydock/templates/kustomized/tutor16/extensions/drydock-jobs/mysql.yml create mode 100644 drydock/templates/kustomized/tutor16/extensions/drydock-jobs/notes.yml create mode 100644 drydock/templates/kustomized/tutor16/extensions/flowers.yml create mode 100644 drydock/templates/kustomized/tutor16/extensions/hpa.yml create mode 100644 drydock/templates/kustomized/tutor16/extensions/ingress.yml create mode 100644 drydock/templates/kustomized/tutor16/extensions/kustomization.yml create mode 100644 drydock/templates/kustomized/tutor16/extensions/multipurpose-jobs.yml create mode 100644 drydock/templates/kustomized/tutor16/extensions/newrelic.ini create mode 100644 drydock/templates/kustomized/tutor16/extensions/overrides.yml create mode 100644 drydock/templates/kustomized/tutor16/kustomization.yml diff --git a/drydock/manifest_builder/infrastructure/tutor_based_manifests.py b/drydock/manifest_builder/infrastructure/tutor_based_manifests.py index 835810f1..27cbf10c 100644 --- a/drydock/manifest_builder/infrastructure/tutor_based_manifests.py +++ b/drydock/manifest_builder/infrastructure/tutor_based_manifests.py @@ -41,18 +41,18 @@ def __init__(self, options: dict) -> None: f"{self.template_root}/base", f"{self.template_root}/extensions", f"{self.template_root}/kustomization.yml", - ] + ] def render(self, root: str, config: DrydockConfig) -> None: """Register drydock custom templates and render a tutor env.""" - with hooks.Contexts.APP("drydock-base").enter(): + with hooks.Contexts.app("drydock-base").enter(): hooks.Filters.ENV_TEMPLATE_ROOTS.add_item(pkg_resources.resource_filename("drydock", "templates")) hooks.Filters.ENV_TEMPLATE_TARGETS.add_items( [(target, "drydock") for target in self.template_targets], ) tutor_env.save(root, config.get_data()) - hooks.Filters.ENV_TEMPLATE_ROOTS.clear(context=hooks.Contexts.APP("drydock-base").name) - hooks.Filters.ENV_TEMPLATE_TARGETS.clear(context=hooks.Contexts.APP("drydock-base").name) + hooks.Filters.ENV_TEMPLATE_ROOTS.clear(context=hooks.Contexts.app("drydock-base").name) + hooks.Filters.ENV_TEMPLATE_TARGETS.clear(context=hooks.Contexts.app("drydock-base").name) def relocate_env(self, src: str, dst: str) -> None: """Moves the drydock rendered templates and tutor plugins to src. diff --git a/drydock/references/tutor_v16.yml b/drydock/references/tutor_v16.yml new file mode 100644 index 00000000..239a01e8 --- /dev/null +++ b/drydock/references/tutor_v16.yml @@ -0,0 +1,10 @@ +# +# The tutor16 reference. +--- +drydock: + builder_class: drydock.manifest_builder.application.manifest_builder.ManifestBuilder + config_class: drydock.manifest_builder.infrastructure.tutor_config.TutorExtendedConfig + manifest_class: drydock.manifest_builder.infrastructure.flex_tutor_manifest.FlexibleTutorManifest + manifest_options: + output: "manifest-output" + tutor_templates_version: "kustomized/tutor16" \ No newline at end of file diff --git a/drydock/templates/kustomized/tutor16/base/apps/caddy/Caddyfile b/drydock/templates/kustomized/tutor16/base/apps/caddy/Caddyfile new file mode 100644 index 00000000..114f49cf --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/apps/caddy/Caddyfile @@ -0,0 +1,83 @@ +# Global configuration +{ + + {% if not ENABLE_WEB_PROXY %} + # Enable proxying from all servers by default. Otherwise, X-Forwarded-* headers will + # be overwritten. + # https://caddyserver.com/docs/caddyfile/directives/reverse_proxy#defaults + servers { + trusted_proxies static 0.0.0.0/0 ::/0 + } + {% endif %} + {{ patch("caddyfile-global")|indent(4) }} +} + +# proxy directive snippet (with logging) to be used as follows: +# +# import proxy "containername:port" +(proxy) { + log { + output stdout + format filter { + wrap json + fields { + common_log delete + request>headers delete + resp_headers delete + tls delete + } + } + } + + # This will compress requests that matches the default criteria set by Caddy. + # see https://caddyserver.com/docs/caddyfile/directives/encode + # for information about the defaults; i.e. how/when this will be applied. + encode gzip + + reverse_proxy {args.0} { + header_up X-Forwarded-Port {{ 443 if ENABLE_HTTPS else 80 }} + } +} + +{{ LMS_HOST }}{$default_site_port}, {{ PREVIEW_LMS_HOST }}{$default_site_port} { + @favicon_matcher { + path_regexp ^/favicon.ico$ + } + rewrite @favicon_matcher /theming/asset/images/favicon.ico + + # Limit profile image upload size + handle_path /api/profile_images/*/*/upload { + request_body { + max_size 1MB + } + } + + import proxy "lms:8000" + + {{ patch("caddyfile-lms")|indent(4) }} + + handle_path /* { + request_body { + max_size 4MB + } + } +} + +{{ CMS_HOST }}{$default_site_port} { + @favicon_matcher { + path_regexp ^/favicon.ico$ + } + rewrite @favicon_matcher /theming/asset/images/favicon.ico + + import proxy "cms:8000" + + {{ patch("caddyfile-cms")|indent(4) }} + + handle_path /* { + request_body { + max_size 250MB + } + } +} + +{{ patch("caddyfile") }} diff --git a/drydock/templates/kustomized/tutor16/base/apps/openedx/config/cms.env.yml b/drydock/templates/kustomized/tutor16/base/apps/openedx/config/cms.env.yml new file mode 100644 index 00000000..c062b2c2 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/apps/openedx/config/cms.env.yml @@ -0,0 +1,40 @@ +SITE_NAME: "{{ CMS_HOST }}" +BOOK_URL: "" +LOG_DIR: "/openedx/data/logs" +LOGGING_ENV: "sandbox" +OAUTH_OIDC_ISSUER: "{{ JWT_COMMON_ISSUER }}" +PLATFORM_NAME: "{{ PLATFORM_NAME }}" +FEATURES: + {{ patch("common-env-features")|indent(2) }} + {{ patch("cms-env-features")|indent(2) }} + CERTIFICATES_HTML_VIEW: true + PREVIEW_LMS_BASE: "{{ PREVIEW_LMS_HOST }}" + ENABLE_COURSEWARE_INDEX: true + ENABLE_CSMH_EXTENDED: false + ENABLE_LEARNER_RECORDS: false + ENABLE_LIBRARY_INDEX: true + MILESTONES_APP: true + ENABLE_PREREQUISITE_COURSES: true +LMS_ROOT_URL: "{{ "https" if ENABLE_HTTPS else "http" }}://{{ LMS_HOST }}" +CMS_ROOT_URL: "{{ "https" if ENABLE_HTTPS else "http" }}://{{ CMS_HOST }}" +CMS_BASE: "{{ CMS_HOST }}" +LMS_BASE: "{{ LMS_HOST }}" +CONTACT_EMAIL: "{{ CONTACT_EMAIL }}" +CELERY_BROKER_TRANSPORT: "redis" +CELERY_BROKER_HOSTNAME: "{{ REDIS_HOST }}:{{ REDIS_PORT }}" +CELERY_BROKER_VHOST: "{{ OPENEDX_CELERY_REDIS_DB }}" +CELERY_BROKER_USER: "{{ REDIS_USERNAME }}" +CELERY_BROKER_PASSWORD: "{{ REDIS_PASSWORD }}" +ALTERNATE_WORKER_QUEUES: "lms" +ENABLE_COMPREHENSIVE_THEMING: true +COMPREHENSIVE_THEME_DIRS: ["/openedx/themes"] +STATIC_ROOT_BASE: "/openedx/staticfiles" +EMAIL_BACKEND: "django.core.mail.backends.smtp.EmailBackend" +EMAIL_HOST: "{{ SMTP_HOST }}" +EMAIL_PORT: {{ SMTP_PORT }} +EMAIL_USE_TLS: {{ "true" if SMTP_USE_TLS else "false" }} +HTTPS: "{{ "on" if ENABLE_HTTPS else "off" }}" +LANGUAGE_CODE: "{{ LANGUAGE_CODE }}" +SESSION_COOKIE_DOMAIN: "{{ CMS_HOST }}" +{{ patch("cms-env") }} +{% include "apps/openedx/config/partials/auth.yml" %} diff --git a/drydock/templates/kustomized/tutor16/base/apps/openedx/config/lms.env.yml b/drydock/templates/kustomized/tutor16/base/apps/openedx/config/lms.env.yml new file mode 100644 index 00000000..a1f93eb9 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/apps/openedx/config/lms.env.yml @@ -0,0 +1,47 @@ +SITE_NAME: "{{ LMS_HOST }}" +BOOK_URL: "" +LOG_DIR: "/openedx/data/logs" +LOGGING_ENV: "sandbox" +OAUTH_OIDC_ISSUER: "{{ JWT_COMMON_ISSUER }}" +PLATFORM_NAME: "{{ PLATFORM_NAME }}" +FEATURES: + {{ patch("common-env-features")|indent(2) }} + {{ patch("lms-env-features")|indent(2) }} + CERTIFICATES_HTML_VIEW: true + PREVIEW_LMS_BASE: "{{ PREVIEW_LMS_HOST }}" + ENABLE_COURSE_DISCOVERY: true + ENABLE_COURSEWARE_SEARCH: true + ENABLE_CSMH_EXTENDED: false + ENABLE_DASHBOARD_SEARCH: true + ENABLE_COMBINED_LOGIN_REGISTRATION: true + ENABLE_GRADE_DOWNLOADS: true + ENABLE_LEARNER_RECORDS: false + ENABLE_MOBILE_REST_API: true + ENABLE_OAUTH2_PROVIDER: true + ENABLE_PREREQUISITE_COURSES: true + ENABLE_THIRD_PARTY_AUTH: true + MILESTONES_APP: true +LMS_ROOT_URL: "{{ "https" if ENABLE_HTTPS else "http" }}://{{ LMS_HOST }}" +CMS_ROOT_URL: "{{ "https" if ENABLE_HTTPS else "http" }}://{{ CMS_HOST }}" +CMS_BASE: "{{ CMS_HOST }}" +LMS_BASE: "{{ LMS_HOST }}" +CONTACT_EMAIL: "{{ CONTACT_EMAIL }}" +CELERY_BROKER_TRANSPORT: "redis" +CELERY_BROKER_HOSTNAME: "{{ REDIS_HOST }}:{{ REDIS_PORT }}" +CELERY_BROKER_VHOST: "{{ OPENEDX_CELERY_REDIS_DB }}" +CELERY_BROKER_USER: "{{ REDIS_USERNAME }}" +CELERY_BROKER_PASSWORD: "{{ REDIS_PASSWORD }}" +ALTERNATE_WORKER_QUEUES: "cms" +ENABLE_COMPREHENSIVE_THEMING: true +COMPREHENSIVE_THEME_DIRS: ["/openedx/themes"] +STATIC_ROOT_BASE: "/openedx/staticfiles" +EMAIL_BACKEND: "django.core.mail.backends.smtp.EmailBackend" +EMAIL_HOST: "{{ SMTP_HOST }}" +EMAIL_PORT: {{ SMTP_PORT }} +EMAIL_USE_TLS: {{ "true" if SMTP_USE_TLS else "false" }} +ACE_ROUTING_KEY: "edx.lms.core.default" +HTTPS: "{{ "on" if ENABLE_HTTPS else "off" }}" +LANGUAGE_CODE: "{{ LANGUAGE_CODE }}" +SESSION_COOKIE_DOMAIN: "{{ LMS_HOST }}" +{{ patch("lms-env") }} +{% include "apps/openedx/config/partials/auth.yml" %} diff --git a/drydock/templates/kustomized/tutor16/base/apps/openedx/config/partials/auth.yml b/drydock/templates/kustomized/tutor16/base/apps/openedx/config/partials/auth.yml new file mode 100644 index 00000000..3e720580 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/apps/openedx/config/partials/auth.yml @@ -0,0 +1,21 @@ +SECRET_KEY: "{{ OPENEDX_SECRET_KEY }}" +AWS_ACCESS_KEY_ID: "{{ OPENEDX_AWS_ACCESS_KEY }}" +AWS_SECRET_ACCESS_KEY: "{{ OPENEDX_AWS_SECRET_ACCESS_KEY }}" +DOC_STORE_CONFIG: null +{{ patch("openedx-auth") }} +XQUEUE_INTERFACE: + django_auth: null + url: null +DATABASES: + default: + ENGINE: "django.db.backends.mysql" + HOST: "{{ MYSQL_HOST }}" + PORT: {{ MYSQL_PORT }} + NAME: "{{ OPENEDX_MYSQL_DATABASE }}" + USER: "{{ OPENEDX_MYSQL_USERNAME }}" + PASSWORD: "{{ OPENEDX_MYSQL_PASSWORD }}" + ATOMIC_REQUESTS: true + OPTIONS: + init_command: "SET sql_mode='STRICT_TRANS_TABLES'" +EMAIL_HOST_USER: "{{ SMTP_USERNAME }}" +EMAIL_HOST_PASSWORD: "{{ SMTP_PASSWORD }}" diff --git a/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/cms/__init__.py b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/cms/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/cms/development.py b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/cms/development.py new file mode 100644 index 00000000..5ba365da --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/cms/development.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +import os +from cms.envs.devstack import * + +LMS_BASE = "{{ LMS_HOST }}:8000" +LMS_ROOT_URL = "http://" + LMS_BASE + +# Authentication +SOCIAL_AUTH_EDX_OAUTH2_KEY = "{{ CMS_OAUTH2_KEY_SSO_DEV }}" +SOCIAL_AUTH_EDX_OAUTH2_PUBLIC_URL_ROOT = LMS_ROOT_URL + +FEATURES["PREVIEW_LMS_BASE"] = "{{ PREVIEW_LMS_HOST }}:8000" + +{% include "apps/openedx/settings/partials/common_cms.py" %} + +# Setup correct webpack configuration file for development +WEBPACK_CONFIG_PATH = "webpack.dev.config.js" + +{{ patch("openedx-development-settings") }} +{{ patch("openedx-cms-development-settings") }} diff --git a/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/cms/production.py b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/cms/production.py new file mode 100644 index 00000000..03cae79b --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/cms/production.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +import os +from cms.envs.production import * + +{% include "apps/openedx/settings/partials/common_cms.py" %} + +ALLOWED_HOSTS = [ + ENV_TOKENS.get("CMS_BASE"), + "cms", +] +CORS_ORIGIN_WHITELIST.append("{% if ENABLE_HTTPS %}https{% else %}http{% endif %}://{{ CMS_HOST }}") + +# Authentication +SOCIAL_AUTH_EDX_OAUTH2_KEY = "{{ CMS_OAUTH2_KEY_SSO }}" +SOCIAL_AUTH_EDX_OAUTH2_PUBLIC_URL_ROOT = "{% if ENABLE_HTTPS %}https{% else %}http{% endif %}://{{ LMS_HOST }}" + +{{ patch("openedx-cms-production-settings") }} diff --git a/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/cms/test.py b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/cms/test.py new file mode 100644 index 00000000..14a3e3db --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/cms/test.py @@ -0,0 +1,3 @@ +from cms.envs.test import * + +{% include "apps/openedx/settings/partials/common_test.py" %} diff --git a/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/lms/__init__.py b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/lms/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/lms/development.py b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/lms/development.py new file mode 100644 index 00000000..ed0c2773 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/lms/development.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +import os +from lms.envs.devstack import * + +{% include "apps/openedx/settings/partials/common_lms.py" %} + +# Setup correct webpack configuration file for development +WEBPACK_CONFIG_PATH = "webpack.dev.config.js" + +LMS_BASE = "{{ LMS_HOST}}:8000" +LMS_ROOT_URL = "http://{}".format(LMS_BASE) +LMS_INTERNAL_ROOT_URL = LMS_ROOT_URL +SITE_NAME = LMS_BASE +CMS_BASE = "{{ CMS_HOST}}:8001" +CMS_ROOT_URL = "http://{}".format(CMS_BASE) +LOGIN_REDIRECT_WHITELIST.append(CMS_BASE) + +# Session cookie +SESSION_COOKIE_DOMAIN = "{{ LMS_HOST }}" +SESSION_COOKIE_SECURE = False +CSRF_COOKIE_SECURE = False +SESSION_COOKIE_SAMESITE = "Lax" + +# CMS authentication +IDA_LOGOUT_URI_LIST.append("http://{{ CMS_HOST }}:8001/logout/") + +FEATURES["ENABLE_COURSEWARE_MICROFRONTEND"] = False + +# Disable enterprise integration +FEATURES["ENABLE_ENTERPRISE_INTEGRATION"] = False +SYSTEM_WIDE_ROLE_CLASSES.remove("enterprise.SystemWideEnterpriseUserRoleAssignment") + +LOGGING["loggers"]["oauth2_provider"] = { + "handlers": ["console"], + "level": "DEBUG" +} + +{{ patch("openedx-development-settings") }} +{{ patch("openedx-lms-development-settings") }} diff --git a/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/lms/production.py b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/lms/production.py new file mode 100644 index 00000000..6c2793b3 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/lms/production.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +import os +from lms.envs.production import * + +{% include "apps/openedx/settings/partials/common_lms.py" %} + +ALLOWED_HOSTS = [ + ENV_TOKENS.get("LMS_BASE"), + FEATURES["PREVIEW_LMS_BASE"], + "lms", +] +CORS_ORIGIN_WHITELIST.append("{% if ENABLE_HTTPS %}https{% else %}http{% endif %}://{{ LMS_HOST }}") + +{% if ENABLE_HTTPS %} +# Properly set the "secure" attribute on session/csrf cookies. This is required in +# Chrome to support samesite=none cookies. +SESSION_COOKIE_SECURE = True +CSRF_COOKIE_SECURE = True +SESSION_COOKIE_SAMESITE = "None" +{% else %} +# When we cannot provide secure session/csrf cookies, we must disable samesite=none +SESSION_COOKIE_SECURE = False +CSRF_COOKIE_SECURE = False +SESSION_COOKIE_SAMESITE = "Lax" +{% endif %} + +# CMS authentication +IDA_LOGOUT_URI_LIST.append("{% if ENABLE_HTTPS %}https{% else %}http{% endif %}://{{ CMS_HOST }}/logout/") + +# Required to display all courses on start page +SEARCH_SKIP_ENROLLMENT_START_DATE_FILTERING = True + +{{ patch("openedx-lms-production-settings") }} diff --git a/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/lms/test.py b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/lms/test.py new file mode 100644 index 00000000..a5fc3518 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/lms/test.py @@ -0,0 +1,3 @@ +from lms.envs.test import * + +{% include "apps/openedx/settings/partials/common_test.py" %} diff --git a/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/partials/common_all.py b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/partials/common_all.py new file mode 100644 index 00000000..f23c8e14 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/partials/common_all.py @@ -0,0 +1,223 @@ +####### Settings common to LMS and CMS +import json +import os + +from xmodule.modulestore.modulestore_settings import update_module_store_settings + +# Mongodb connection parameters: simply modify `mongodb_parameters` to affect all connections to MongoDb. +mongodb_parameters = { + "db": "{{ MONGODB_DATABASE }}", + "host": "{{ MONGODB_HOST }}", + "port": {{ MONGODB_PORT }}, + "user": {% if MONGODB_USERNAME %}"{{ MONGODB_USERNAME }}"{% else %}None{% endif %}, + "password": {% if MONGODB_PASSWORD %}"{{ MONGODB_PASSWORD }}"{% else %}None{% endif %}, + # Connection/Authentication + "ssl": {{ MONGODB_USE_SSL }}, + "authSource": "{{ MONGODB_AUTH_SOURCE }}", + "replicaSet": {% if MONGODB_REPLICA_SET %}"{{ MONGODB_REPLICA_SET }}"{% else %}None{% endif %}, + {% if MONGODB_AUTH_MECHANISM %}"authMechanism": "{{ MONGODB_AUTH_MECHANISM }}",{% endif %} +} +DOC_STORE_CONFIG = mongodb_parameters +CONTENTSTORE = { + "ENGINE": "xmodule.contentstore.mongo.MongoContentStore", + "ADDITIONAL_OPTIONS": {}, + "DOC_STORE_CONFIG": DOC_STORE_CONFIG +} +# Load module store settings from config files +update_module_store_settings(MODULESTORE, doc_store_settings=DOC_STORE_CONFIG) +DATA_DIR = "/openedx/data/modulestore" + +for store in MODULESTORE["default"]["OPTIONS"]["stores"]: + store["OPTIONS"]["fs_root"] = DATA_DIR + +# Behave like memcache when it comes to connection errors +DJANGO_REDIS_IGNORE_EXCEPTIONS = True + +# Elasticsearch connection parameters +ELASTIC_SEARCH_CONFIG = [{ + {% if ELASTICSEARCH_SCHEME == "https" %}"use_ssl": True,{% endif %} + "host": "{{ ELASTICSEARCH_HOST }}", + "port": {{ ELASTICSEARCH_PORT }}, +}] + +# Common cache config +CACHES = { + "default": { + "KEY_PREFIX": "default", + "VERSION": "1", + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": "redis://{% if REDIS_USERNAME and REDIS_PASSWORD %}{{ REDIS_USERNAME }}:{{ REDIS_PASSWORD }}{% endif %}@{{ REDIS_HOST }}:{{ REDIS_PORT }}/{{ OPENEDX_CACHE_REDIS_DB }}", + }, + "general": { + "KEY_PREFIX": "general", + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": "redis://{% if REDIS_USERNAME and REDIS_PASSWORD %}{{ REDIS_USERNAME }}:{{ REDIS_PASSWORD }}{% endif %}@{{ REDIS_HOST }}:{{ REDIS_PORT }}/{{ OPENEDX_CACHE_REDIS_DB }}", + }, + "mongo_metadata_inheritance": { + "KEY_PREFIX": "mongo_metadata_inheritance", + "TIMEOUT": 300, + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": "redis://{% if REDIS_USERNAME and REDIS_PASSWORD %}{{ REDIS_USERNAME }}:{{ REDIS_PASSWORD }}{% endif %}@{{ REDIS_HOST }}:{{ REDIS_PORT }}/{{ OPENEDX_CACHE_REDIS_DB }}", + }, + "configuration": { + "KEY_PREFIX": "configuration", + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": "redis://{% if REDIS_USERNAME and REDIS_PASSWORD %}{{ REDIS_USERNAME }}:{{ REDIS_PASSWORD }}{% endif %}@{{ REDIS_HOST }}:{{ REDIS_PORT }}/{{ OPENEDX_CACHE_REDIS_DB }}", + }, + "celery": { + "KEY_PREFIX": "celery", + "TIMEOUT": 7200, + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": "redis://{% if REDIS_USERNAME and REDIS_PASSWORD %}{{ REDIS_USERNAME }}:{{ REDIS_PASSWORD }}{% endif %}@{{ REDIS_HOST }}:{{ REDIS_PORT }}/{{ OPENEDX_CACHE_REDIS_DB }}", + }, + "course_structure_cache": { + "KEY_PREFIX": "course_structure", + "TIMEOUT": 7200, + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": "redis://{% if REDIS_USERNAME and REDIS_PASSWORD %}{{ REDIS_USERNAME }}:{{ REDIS_PASSWORD }}{% endif %}@{{ REDIS_HOST }}:{{ REDIS_PORT }}/{{ OPENEDX_CACHE_REDIS_DB }}", + }, +} + +# The default Django contrib site is the one associated to the LMS domain name. 1 is +# usually "example.com", so it's the next available integer. +SITE_ID = 2 + +# Contact addresses +CONTACT_MAILING_ADDRESS = "{{ PLATFORM_NAME }} - {% if ENABLE_HTTPS %}https{% else %}http{% endif %}://{{ LMS_HOST }}" +DEFAULT_FROM_EMAIL = ENV_TOKENS.get("DEFAULT_FROM_EMAIL", ENV_TOKENS["CONTACT_EMAIL"]) +DEFAULT_FEEDBACK_EMAIL = ENV_TOKENS.get("DEFAULT_FEEDBACK_EMAIL", ENV_TOKENS["CONTACT_EMAIL"]) +SERVER_EMAIL = ENV_TOKENS.get("SERVER_EMAIL", ENV_TOKENS["CONTACT_EMAIL"]) +TECH_SUPPORT_EMAIL = ENV_TOKENS.get("TECH_SUPPORT_EMAIL", ENV_TOKENS["CONTACT_EMAIL"]) +CONTACT_EMAIL = ENV_TOKENS.get("CONTACT_EMAIL", ENV_TOKENS["CONTACT_EMAIL"]) +BUGS_EMAIL = ENV_TOKENS.get("BUGS_EMAIL", ENV_TOKENS["CONTACT_EMAIL"]) +UNIVERSITY_EMAIL = ENV_TOKENS.get("UNIVERSITY_EMAIL", ENV_TOKENS["CONTACT_EMAIL"]) +PRESS_EMAIL = ENV_TOKENS.get("PRESS_EMAIL", ENV_TOKENS["CONTACT_EMAIL"]) +PAYMENT_SUPPORT_EMAIL = ENV_TOKENS.get("PAYMENT_SUPPORT_EMAIL", ENV_TOKENS["CONTACT_EMAIL"]) +BULK_EMAIL_DEFAULT_FROM_EMAIL = ENV_TOKENS.get("BULK_EMAIL_DEFAULT_FROM_EMAIL", ENV_TOKENS["CONTACT_EMAIL"]) +API_ACCESS_MANAGER_EMAIL = ENV_TOKENS.get("API_ACCESS_MANAGER_EMAIL", ENV_TOKENS["CONTACT_EMAIL"]) +API_ACCESS_FROM_EMAIL = ENV_TOKENS.get("API_ACCESS_FROM_EMAIL", ENV_TOKENS["CONTACT_EMAIL"]) + +# Get rid completely of coursewarehistoryextended, as we do not use the CSMH database +INSTALLED_APPS.remove("lms.djangoapps.coursewarehistoryextended") +DATABASE_ROUTERS.remove( + "openedx.core.lib.django_courseware_routers.StudentModuleHistoryExtendedRouter" +) + +# Set uploaded media file path +MEDIA_ROOT = "/openedx/media/" + +# Video settings +VIDEO_IMAGE_SETTINGS["STORAGE_KWARGS"]["location"] = MEDIA_ROOT +VIDEO_TRANSCRIPTS_SETTINGS["STORAGE_KWARGS"]["location"] = MEDIA_ROOT + +GRADES_DOWNLOAD = { + "STORAGE_TYPE": "", + "STORAGE_KWARGS": { + "base_url": "/media/grades/", + "location": "/openedx/media/grades", + }, +} + +ORA2_FILEUPLOAD_BACKEND = "filesystem" +ORA2_FILEUPLOAD_ROOT = "/openedx/data/ora2" +ORA2_FILEUPLOAD_CACHE_NAME = "ora2-storage" + +# Change syslog-based loggers which don't work inside docker containers +LOGGING["handlers"]["local"] = { + "class": "logging.handlers.WatchedFileHandler", + "filename": os.path.join(LOG_DIR, "all.log"), + "formatter": "standard", +} +LOGGING["handlers"]["tracking"] = { + "level": "DEBUG", + "class": "logging.handlers.WatchedFileHandler", + "filename": os.path.join(LOG_DIR, "tracking.log"), + "formatter": "standard", +} +LOGGING["loggers"]["tracking"]["handlers"] = ["console", "local", "tracking"] +# Silence some loggers (note: we must attempt to get rid of these when upgrading from one release to the next) + +import warnings +from django.utils.deprecation import RemovedInDjango40Warning, RemovedInDjango41Warning +warnings.filterwarnings("ignore", category=RemovedInDjango40Warning) +warnings.filterwarnings("ignore", category=RemovedInDjango41Warning) +warnings.filterwarnings("ignore", category=DeprecationWarning, module="lms.djangoapps.course_wiki.plugins.markdownedx.wiki_plugin") +warnings.filterwarnings("ignore", category=DeprecationWarning, module="wiki.plugins.links.wiki_plugin") + +# Email +EMAIL_USE_SSL = {{ SMTP_USE_SSL }} +# Forward all emails from edX's Automated Communication Engine (ACE) to django. +ACE_ENABLED_CHANNELS = ["django_email"] +ACE_CHANNEL_DEFAULT_EMAIL = "django_email" +ACE_CHANNEL_TRANSACTIONAL_EMAIL = "django_email" +EMAIL_FILE_PATH = "/tmp/openedx/emails" + +# Language/locales +LOCALE_PATHS.append("/openedx/locale/contrib/locale") +LOCALE_PATHS.append("/openedx/locale/user/locale") +LANGUAGE_COOKIE_NAME = "openedx-language-preference" + +# Allow the platform to include itself in an iframe +X_FRAME_OPTIONS = "SAMEORIGIN" + +{% set jwt_rsa_key | rsa_import_key %}{{ JWT_RSA_PRIVATE_KEY }}{% endset %} +JWT_AUTH["JWT_ISSUER"] = "{{ JWT_COMMON_ISSUER }}" +JWT_AUTH["JWT_AUDIENCE"] = "{{ JWT_COMMON_AUDIENCE }}" +JWT_AUTH["JWT_SECRET_KEY"] = "{{ JWT_COMMON_SECRET_KEY }}" +JWT_AUTH["JWT_PRIVATE_SIGNING_JWK"] = json.dumps( + { + "kid": "openedx", + "kty": "RSA", + "e": "{{ jwt_rsa_key.e|long_to_base64 }}", + "d": "{{ jwt_rsa_key.d|long_to_base64 }}", + "n": "{{ jwt_rsa_key.n|long_to_base64 }}", + "p": "{{ jwt_rsa_key.p|long_to_base64 }}", + "q": "{{ jwt_rsa_key.q|long_to_base64 }}", + } +) +JWT_AUTH["JWT_PUBLIC_SIGNING_JWK_SET"] = json.dumps( + { + "keys": [ + { + "kid": "openedx", + "kty": "RSA", + "e": "{{ jwt_rsa_key.e|long_to_base64 }}", + "n": "{{ jwt_rsa_key.n|long_to_base64 }}", + } + ] + } +) +JWT_AUTH["JWT_ISSUERS"] = [ + { + "ISSUER": "{{ JWT_COMMON_ISSUER }}", + "AUDIENCE": "{{ JWT_COMMON_AUDIENCE }}", + "SECRET_KEY": "{{ OPENEDX_SECRET_KEY }}" + } +] + +# Enable/Disable some features globally +FEATURES["ENABLE_DISCUSSION_SERVICE"] = False +FEATURES["PREVENT_CONCURRENT_LOGINS"] = False +FEATURES["ENABLE_CORS_HEADERS"] = True + +# CORS +CORS_ALLOW_CREDENTIALS = True +CORS_ORIGIN_ALLOW_ALL = False +CORS_ALLOW_INSECURE = {% if ENABLE_HTTPS %}False{% else %}True{% endif %} +CORS_ALLOW_HEADERS = corsheaders_default_headers + ('use-jwt-cookie',) + +# Add your MFE and third-party app domains here +CORS_ORIGIN_WHITELIST = [] + +# Disable codejail support +# explicitely configuring python is necessary to prevent unsafe calls +import codejail.jail_code +codejail.jail_code.configure("python", "nonexistingpythonbinary", user=None) +# another configuration entry is required to override prod/dev settings +CODE_JAIL = { + "python_bin": "nonexistingpythonbinary", + "user": None, +} + +{{ patch("openedx-common-settings") }} +######## End of settings common to LMS and CMS diff --git a/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/partials/common_cms.py b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/partials/common_cms.py new file mode 100644 index 00000000..c513de53 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/partials/common_cms.py @@ -0,0 +1,30 @@ +{% include "apps/openedx/settings/partials/common_all.py" %} + +######## Common CMS settings +STUDIO_NAME = "{{ PLATFORM_NAME }} - Studio" + +CACHES["staticfiles"] = { + "KEY_PREFIX": "staticfiles_cms", + "BACKEND": "django.core.cache.backends.locmem.LocMemCache", + "LOCATION": "staticfiles_cms", +} + +# Authentication +SOCIAL_AUTH_EDX_OAUTH2_SECRET = "{{ CMS_OAUTH2_SECRET }}" +SOCIAL_AUTH_EDX_OAUTH2_URL_ROOT = "http://lms:8000" +SOCIAL_AUTH_REDIRECT_IS_HTTPS = False # scheme is correctly included in redirect_uri +SESSION_COOKIE_NAME = "studio_session_id" + +MAX_ASSET_UPLOAD_FILE_SIZE_IN_MB = 100 + +FRONTEND_LOGIN_URL = LMS_ROOT_URL + '/login' +FRONTEND_REGISTER_URL = LMS_ROOT_URL + '/register' + +# Create folders if necessary +for folder in [LOG_DIR, MEDIA_ROOT, STATIC_ROOT_BASE]: + if not os.path.exists(folder): + os.makedirs(folder) + +{{ patch("openedx-cms-common-settings") }} + +######## End of common CMS settings diff --git a/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/partials/common_lms.py b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/partials/common_lms.py new file mode 100644 index 00000000..5461d65a --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/partials/common_lms.py @@ -0,0 +1,52 @@ +{% include "apps/openedx/settings/partials/common_all.py" %} + +######## Common LMS settings +LOGIN_REDIRECT_WHITELIST = ["{{ CMS_HOST }}"] + +# Better layout of honor code/tos links during registration +REGISTRATION_EXTRA_FIELDS["terms_of_service"] = "required" +REGISTRATION_EXTRA_FIELDS["honor_code"] = "hidden" + +# Fix media files paths +PROFILE_IMAGE_BACKEND["options"]["location"] = os.path.join( + MEDIA_ROOT, "profile-images/" +) + +COURSE_CATALOG_VISIBILITY_PERMISSION = "see_in_catalog" +COURSE_ABOUT_VISIBILITY_PERMISSION = "see_about_page" + +# Allow insecure oauth2 for local interaction with local containers +OAUTH_ENFORCE_SECURE = False + +# Email settings +DEFAULT_EMAIL_LOGO_URL = LMS_ROOT_URL + "/theming/asset/images/logo.png" +BULK_EMAIL_SEND_USING_EDX_ACE = True +FEATURES["ENABLE_FOOTER_MOBILE_APP_LINKS"] = False + +# Branding +MOBILE_STORE_ACE_URLS = {} +SOCIAL_MEDIA_FOOTER_ACE_URLS = {} + +# Make it possible to hide courses by default from the studio +SEARCH_SKIP_SHOW_IN_CATALOG_FILTERING = False + +# Caching +CACHES["staticfiles"] = { + "KEY_PREFIX": "staticfiles_lms", + "BACKEND": "django.core.cache.backends.locmem.LocMemCache", + "LOCATION": "staticfiles_lms", +} +CACHES["ora2-storage"] = { + "KEY_PREFIX": "ora2-storage", + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": "redis://{% if REDIS_USERNAME and REDIS_PASSWORD %}{{ REDIS_USERNAME }}:{{ REDIS_PASSWORD }}{% endif %}@{{ REDIS_HOST }}:{{ REDIS_PORT }}/{{ OPENEDX_CACHE_REDIS_DB }}", +} + +# Create folders if necessary +for folder in [DATA_DIR, LOG_DIR, MEDIA_ROOT, STATIC_ROOT_BASE, ORA2_FILEUPLOAD_ROOT]: + if not os.path.exists(folder): + os.makedirs(folder, exist_ok=True) + +{{ patch("openedx-lms-common-settings") }} + +######## End of common LMS settings diff --git a/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/partials/common_test.py b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/partials/common_test.py new file mode 100644 index 00000000..290ac292 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/apps/openedx/settings/partials/common_test.py @@ -0,0 +1,3 @@ +# Fix MongoDb connection credentials +DOC_STORE_CONFIG["user"] = None +DOC_STORE_CONFIG["password"] = None diff --git a/drydock/templates/kustomized/tutor16/base/apps/redis/redis.conf b/drydock/templates/kustomized/tutor16/base/apps/redis/redis.conf new file mode 100644 index 00000000..cfdf709d --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/apps/redis/redis.conf @@ -0,0 +1,41 @@ +# https://raw.githubusercontent.com/redis/redis/6.0/redis.conf +port 6379 + +tcp-backlog 511 +timeout 0 +tcp-keepalive 300 +daemonize no +supervised no +pidfile /var/run/redis_6379.pid +loglevel notice +logfile "" +databases 16 + +################################ SNAPSHOTTING ################################ +# +# Save the DB on disk: +# +# save + +save 900 1 +save 300 10 +save 60 10000 + +stop-writes-on-bgsave-error yes +rdbcompression yes +rdbchecksum yes +dir /openedx/redis/data/ +dbfilename dump.rdb +rdb-del-sync-files no + +############################## APPEND ONLY MODE ############################### + +# http://redis.io/topics/persistence +appendonly yes +appendfilename "appendonly.aof" +appendfsync everysec +no-appendfsync-on-rewrite no +auto-aof-rewrite-percentage 100 +auto-aof-rewrite-min-size 64mb +aof-load-truncated yes +aof-use-rdb-preamble yes diff --git a/drydock/templates/kustomized/tutor16/base/k8s/deployments.yml b/drydock/templates/kustomized/tutor16/base/k8s/deployments.yml new file mode 100644 index 00000000..90bb987e --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/k8s/deployments.yml @@ -0,0 +1,467 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: caddy + labels: + app.kubernetes.io/name: caddy +spec: + selector: + matchLabels: + app.kubernetes.io/name: caddy + template: + metadata: + labels: + app.kubernetes.io/name: caddy + spec: + {%- if ENABLE_WEB_PROXY %} + # This Deployment uses a persistent volume claim. This requires + # that in order to enable rolling updates (i.e. use a deployment + # strategy other than Replace), we schedule the new Pod to the + # same node as the original Pod. + affinity: + podAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app.kubernetes.io/name + operator: In + values: + - caddy + topologyKey: "kubernetes.io/hostname" + {%- endif %} + containers: + - name: caddy + image: {{ DOCKER_IMAGE_CADDY }} + env: + - name: default_site_port + value: "{% if not ENABLE_HTTPS or not ENABLE_WEB_PROXY %}:80{% endif %}" + volumeMounts: + - mountPath: /etc/caddy/ + name: config + {%- if ENABLE_WEB_PROXY %} + - mountPath: /data/ + name: data + {%- endif %} + ports: + - containerPort: 80 + {%- if ENABLE_WEB_PROXY %} + - containerPort: 443 + {%- endif %} + volumes: + - name: config + configMap: + name: caddy-config + {%- if ENABLE_WEB_PROXY %} + - name: data + persistentVolumeClaim: + claimName: caddy + {%- endif %} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: cms + labels: + app.kubernetes.io/name: cms +spec: + selector: + matchLabels: + app.kubernetes.io/name: cms + template: + metadata: + labels: + app.kubernetes.io/name: cms + spec: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + containers: + - name: cms + image: {{ DOCKER_IMAGE_OPENEDX }} + env: + - name: SERVICE_VARIANT + value: cms + - name: DJANGO_SETTINGS_MODULE + value: cms.envs.tutor.production + ports: + - containerPort: 8000 + volumeMounts: + - mountPath: /openedx/edx-platform/lms/envs/tutor/ + name: settings-lms + - mountPath: /openedx/edx-platform/cms/envs/tutor/ + name: settings-cms + - mountPath: /openedx/config + name: config + resources: + requests: + memory: 2Gi + securityContext: + allowPrivilegeEscalation: false + volumes: + - name: settings-lms + configMap: + name: openedx-settings-lms + - name: settings-cms + configMap: + name: openedx-settings-cms + - name: config + configMap: + name: openedx-config +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: cms-worker + labels: + app.kubernetes.io/name: cms-worker + annotations: + argocd.argoproj.io/sync-wave: "4" +spec: + selector: + matchLabels: + app.kubernetes.io/name: cms-worker + template: + metadata: + labels: + app.kubernetes.io/name: cms-worker + spec: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + containers: + - name: cms-worker + image: {{ DOCKER_IMAGE_OPENEDX }} + args: ["celery", "--app=cms.celery", "worker", "--loglevel=info", "--hostname=edx.cms.core.default.%%h", "--max-tasks-per-child", "100", "--exclude-queues=edx.lms.core.default"] + env: + - name: SERVICE_VARIANT + value: cms + - name: DJANGO_SETTINGS_MODULE + value: cms.envs.tutor.production + volumeMounts: + - mountPath: /openedx/edx-platform/lms/envs/tutor/ + name: settings-lms + - mountPath: /openedx/edx-platform/cms/envs/tutor/ + name: settings-cms + - mountPath: /openedx/config + name: config + securityContext: + allowPrivilegeEscalation: false + volumes: + - name: settings-lms + configMap: + name: openedx-settings-lms + - name: settings-cms + configMap: + name: openedx-settings-cms + - name: config + configMap: + name: openedx-config +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: lms + labels: + app.kubernetes.io/name: lms +spec: + selector: + matchLabels: + app.kubernetes.io/name: lms + template: + metadata: + labels: + app.kubernetes.io/name: lms + spec: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + containers: + - name: lms + image: {{ DOCKER_IMAGE_OPENEDX }} + env: + - name: SERVICE_VARIANT + value: lms + - name: DJANGO_SETTINGS_MODULE + value: lms.envs.tutor.production + ports: + - containerPort: 8000 + volumeMounts: + - mountPath: /openedx/edx-platform/lms/envs/tutor/ + name: settings-lms + - mountPath: /openedx/edx-platform/cms/envs/tutor/ + name: settings-cms + - mountPath: /openedx/config + name: config + resources: + requests: + memory: 2Gi + securityContext: + allowPrivilegeEscalation: false + volumes: + - name: settings-lms + configMap: + name: openedx-settings-lms + - name: settings-cms + configMap: + name: openedx-settings-cms + - name: config + configMap: + name: openedx-config +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: lms-worker + labels: + app.kubernetes.io/name: lms-worker + annotations: + argocd.argoproj.io/sync-wave: "4" +spec: + selector: + matchLabels: + app.kubernetes.io/name: lms-worker + template: + metadata: + labels: + app.kubernetes.io/name: lms-worker + spec: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + containers: + - name: lms-worker + image: {{ DOCKER_IMAGE_OPENEDX }} + args: ["celery", "--app=lms.celery", "worker", "--loglevel=info", "--hostname=edx.lms.core.default.%%h", "--max-tasks-per-child=100", "--exclude-queues=edx.cms.core.default"] + env: + - name: SERVICE_VARIANT + value: lms + - name: DJANGO_SETTINGS_MODULE + value: lms.envs.tutor.production + volumeMounts: + - mountPath: /openedx/edx-platform/lms/envs/tutor/ + name: settings-lms + - mountPath: /openedx/edx-platform/cms/envs/tutor/ + name: settings-cms + - mountPath: /openedx/config + name: config + securityContext: + allowPrivilegeEscalation: false + volumes: + - name: settings-lms + configMap: + name: openedx-settings-lms + - name: settings-cms + configMap: + name: openedx-settings-cms + - name: config + configMap: + name: openedx-config +{% if RUN_ELASTICSEARCH %} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: elasticsearch + labels: + app.kubernetes.io/name: elasticsearch +spec: + selector: + matchLabels: + app.kubernetes.io/name: elasticsearch + strategy: + type: Recreate + template: + metadata: + labels: + app.kubernetes.io/name: elasticsearch + spec: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + fsGroupChangePolicy: "OnRootMismatch" + containers: + - name: elasticsearch + image: {{ DOCKER_IMAGE_ELASTICSEARCH }} + env: + - name: cluster.name + value: "openedx" + - name: bootstrap.memory_lock + value: "true" + - name: discovery.type + value: "single-node" + - name: ES_JAVA_OPTS + value: "-Xms{{ ELASTICSEARCH_HEAP_SIZE }} -Xmx{{ ELASTICSEARCH_HEAP_SIZE }}" + - name: TAKE_FILE_OWNERSHIP + value: "1" + ports: + - containerPort: 9200 + securityContext: + allowPrivilegeEscalation: false + volumeMounts: + - mountPath: /usr/share/elasticsearch/data + name: data + volumes: + - name: data + persistentVolumeClaim: + claimName: elasticsearch +{% endif %} +{% if RUN_MONGODB %} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mongodb + labels: + app.kubernetes.io/name: mongodb +spec: + selector: + matchLabels: + app.kubernetes.io/name: mongodb + strategy: + type: Recreate + template: + metadata: + labels: + app.kubernetes.io/name: mongodb + spec: + securityContext: + runAsUser: 999 + runAsGroup: 999 + fsGroup: 999 + fsGroupChangePolicy: "OnRootMismatch" + containers: + - name: mongodb + image: {{ DOCKER_IMAGE_MONGODB }} + args: ["mongod", "--nojournal", "--storageEngine", "wiredTiger"] + ports: + - containerPort: 27017 + volumeMounts: + - mountPath: /data/db + name: data + securityContext: + allowPrivilegeEscalation: false + volumes: + - name: data + persistentVolumeClaim: + claimName: mongodb +{% endif %} +{% if RUN_MYSQL %} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mysql + labels: + app.kubernetes.io/name: mysql +spec: + selector: + matchLabels: + app.kubernetes.io/name: mysql + strategy: + type: Recreate + template: + metadata: + labels: + app.kubernetes.io/name: mysql + spec: + securityContext: + runAsUser: 999 + runAsGroup: 999 + fsGroup: 999 + fsGroupChangePolicy: "OnRootMismatch" + containers: + - name: mysql + image: {{ DOCKER_IMAGE_MYSQL }} + # Note the ignore-db-dir: this is because ext4 volumes are created with a lost+found directory in them, which causes mysql + # initialisation to fail + args: ["mysqld", "--character-set-server=utf8", "--collation-server=utf8_general_ci", "--ignore-db-dir=lost+found"] + env: + - name: MYSQL_ROOT_PASSWORD + value: "{{ MYSQL_ROOT_PASSWORD }}" + ports: + - containerPort: 3306 + volumeMounts: + - mountPath: /var/lib/mysql + name: data + securityContext: + allowPrivilegeEscalation: false + volumes: + - name: data + persistentVolumeClaim: + claimName: mysql +{% endif %} +{% if RUN_SMTP %} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: smtp + labels: + app.kubernetes.io/name: smtp +spec: + selector: + matchLabels: + app.kubernetes.io/name: smtp + template: + metadata: + labels: + app.kubernetes.io/name: smtp + spec: + securityContext: + runAsUser: 100 + runAsGroup: 101 + containers: + - name: smtp + image: {{ DOCKER_IMAGE_SMTP }} + ports: + - containerPort: 8025 +{% endif %} +{% if RUN_REDIS %} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: redis + labels: + app.kubernetes.io/name: redis +spec: + selector: + matchLabels: + app.kubernetes.io/name: redis + strategy: + type: Recreate + template: + metadata: + labels: + app.kubernetes.io/name: redis + spec: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + fsGroupChangePolicy: "OnRootMismatch" + containers: + - name: redis + image: {{ DOCKER_IMAGE_REDIS }} + args: ["redis-server", "/openedx/redis/config/redis.conf"] + workingDir: /openedx/redis/data + ports: + - containerPort: {{ REDIS_PORT }} + volumeMounts: + - mountPath: /openedx/redis/config/ + name: config + - mountPath: /openedx/redis/data + name: data + securityContext: + allowPrivilegeEscalation: false + volumes: + - name: config + configMap: + name: redis-config + - name: data + persistentVolumeClaim: + claimName: redis +{% endif %} +{{ patch("k8s-deployments") }} diff --git a/drydock/templates/kustomized/tutor16/base/k8s/jobs.yml b/drydock/templates/kustomized/tutor16/base/k8s/jobs.yml new file mode 100644 index 00000000..7b751360 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/k8s/jobs.yml @@ -0,0 +1,88 @@ +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: lms-job + labels: + app.kubernetes.io/component: job +spec: + template: + spec: + restartPolicy: Never + containers: + - name: lms + image: {{ DOCKER_IMAGE_OPENEDX }} + env: + - name: SERVICE_VARIANT + value: lms + - name: DJANGO_SETTINGS_MODULE + value: lms.envs.tutor.production + volumeMounts: + - mountPath: /openedx/edx-platform/lms/envs/tutor/ + name: settings-lms + - mountPath: /openedx/edx-platform/cms/envs/tutor/ + name: settings-cms + - mountPath: /openedx/config + name: config + volumes: + - name: settings-lms + configMap: + name: openedx-settings-lms + - name: settings-cms + configMap: + name: openedx-settings-cms + - name: config + configMap: + name: openedx-config +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: cms-job + labels: + app.kubernetes.io/component: job +spec: + template: + spec: + restartPolicy: Never + containers: + - name: cms + image: {{ DOCKER_IMAGE_OPENEDX }} + env: + - name: SERVICE_VARIANT + value: cms + - name: DJANGO_SETTINGS_MODULE + value: cms.envs.tutor.production + volumeMounts: + - mountPath: /openedx/edx-platform/lms/envs/tutor/ + name: settings-lms + - mountPath: /openedx/edx-platform/cms/envs/tutor/ + name: settings-cms + - mountPath: /openedx/config + name: config + volumes: + - name: settings-lms + configMap: + name: openedx-settings-lms + - name: settings-cms + configMap: + name: openedx-settings-cms + - name: config + configMap: + name: openedx-config +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: mysql-job + labels: + app.kubernetes.io/component: job +spec: + template: + spec: + restartPolicy: Never + containers: + - name: mysql + image: {{ DOCKER_IMAGE_MYSQL }} + +{{ patch("k8s-jobs") }} diff --git a/drydock/templates/kustomized/tutor16/base/k8s/namespace.yml b/drydock/templates/kustomized/tutor16/base/k8s/namespace.yml new file mode 100644 index 00000000..d1b35d97 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/k8s/namespace.yml @@ -0,0 +1,7 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: {{ K8S_NAMESPACE }} + labels: + app.kubernetes.io/component: namespace \ No newline at end of file diff --git a/drydock/templates/kustomized/tutor16/base/k8s/override.yml b/drydock/templates/kustomized/tutor16/base/k8s/override.yml new file mode 100644 index 00000000..5c105154 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/k8s/override.yml @@ -0,0 +1 @@ +{{ patch("k8s-override") }} diff --git a/drydock/templates/kustomized/tutor16/base/k8s/services.yml b/drydock/templates/kustomized/tutor16/base/k8s/services.yml new file mode 100644 index 00000000..aaaa9b67 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/k8s/services.yml @@ -0,0 +1,145 @@ +{% if ENABLE_WEB_PROXY %} +--- +apiVersion: v1 +kind: Service +metadata: + name: caddy + labels: + app.kubernetes.io/name: caddy + app.kubernetes.io/component: loadbalancer +spec: + type: LoadBalancer + ports: + - port: 80 + name: http + {%- if ENABLE_HTTPS %} + - port: 443 + name: https + {%- endif %} + selector: + app.kubernetes.io/name: caddy +{% else %} +--- +apiVersion: v1 +kind: Service +metadata: + name: caddy + labels: + app.kubernetes.io/name: caddy +spec: + type: ClusterIP + ports: + - port: {{ CADDY_HTTP_PORT }} + name: http + selector: + app.kubernetes.io/name: caddy +{% endif %} +--- +apiVersion: v1 +kind: Service +metadata: + name: cms + labels: + app.kubernetes.io/name: cms +spec: + type: ClusterIP + ports: + - port: 8000 + protocol: TCP + selector: + app.kubernetes.io/name: cms +--- +apiVersion: v1 +kind: Service +metadata: + name: lms + labels: + app.kubernetes.io/name: lms +spec: + type: ClusterIP + ports: + - port: 8000 + protocol: TCP + selector: + app.kubernetes.io/name: lms +{% if RUN_ELASTICSEARCH %} +--- +apiVersion: v1 +kind: Service +metadata: + name: elasticsearch + labels: + app.kubernetes.io/name: elasticsearch +spec: + type: ClusterIP + ports: + - port: 9200 + protocol: TCP + selector: + app.kubernetes.io/name: elasticsearch +{% endif %} +{% if RUN_MONGODB %} +--- +apiVersion: v1 +kind: Service +metadata: + name: mongodb + labels: + app.kubernetes.io/name: mongodb +spec: + type: ClusterIP + ports: + - port: 27017 + protocol: TCP + selector: + app.kubernetes.io/name: mongodb +{% endif %} +{% if RUN_MYSQL %} +--- +apiVersion: v1 +kind: Service +metadata: + name: mysql + labels: + app.kubernetes.io/name: mysql +spec: + type: ClusterIP + ports: + - port: 3306 + protocol: TCP + selector: + app.kubernetes.io/name: mysql +{% endif %} +{% if RUN_REDIS %} +--- +apiVersion: v1 +kind: Service +metadata: + name: redis + labels: + app.kubernetes.io/name: redis +spec: + type: ClusterIP + ports: + - port: {{ REDIS_PORT }} + protocol: TCP + selector: + app.kubernetes.io/name: redis +{% endif %} +{% if RUN_SMTP %} +--- +apiVersion: v1 +kind: Service +metadata: + name: smtp + labels: + app.kubernetes.io/name: smtp +spec: + type: ClusterIP + ports: + - port: 8025 + protocol: TCP + selector: + app.kubernetes.io/name: smtp +{% endif %} +{{ patch("k8s-services") }} diff --git a/drydock/templates/kustomized/tutor16/base/k8s/volumes.yml b/drydock/templates/kustomized/tutor16/base/k8s/volumes.yml new file mode 100644 index 00000000..ffb4b664 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/k8s/volumes.yml @@ -0,0 +1,81 @@ +{% if ENABLE_WEB_PROXY %} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: caddy + labels: + app.kubernetes.io/component: volume + app.kubernetes.io/name: caddy +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +{% endif %} +{% if RUN_ELASTICSEARCH %} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: elasticsearch + labels: + app.kubernetes.io/component: volume + app.kubernetes.io/name: elasticsearch +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 2Gi +{% endif %} +{% if RUN_MONGODB %} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mongodb + labels: + app.kubernetes.io/component: volume + app.kubernetes.io/name: mongodb +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi +{% endif %} +{% if RUN_MYSQL %} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mysql + labels: + app.kubernetes.io/component: volume + app.kubernetes.io/name: mysql +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi +{% endif %} +{% if RUN_REDIS %} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: redis + labels: + app.kubernetes.io/component: volume + app.kubernetes.io/name: redis +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +{% endif %} +{{ patch("k8s-volumes") }} \ No newline at end of file diff --git a/drydock/templates/kustomized/tutor16/base/kustomization.yml b/drydock/templates/kustomized/tutor16/base/kustomization.yml new file mode 100644 index 00000000..6fb3a628 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/base/kustomization.yml @@ -0,0 +1,70 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- k8s/namespace.yml +- k8s/deployments.yml +- k8s/services.yml +- k8s/volumes.yml + +{{ patch("kustomization-resources") }} + +# namespace to deploy all Resources to +namespace: {{ K8S_NAMESPACE }} + +# annotations added to all Resources +# https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/commonannotations/ +commonAnnotations: + app.kubernetes.io/version: {{ TUTOR_VERSION }} + +# labels (and label selectors) added to all Resources +# https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/ +# https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/commonlabels/ +commonLabels: + app.kubernetes.io/instance: openedx-{{ ID }} + app.kubernetes.io/part-of: openedx + app.kubernetes.io/managed-by: tutor + {{ patch("kustomization-commonlabels")|indent(2) }} + +configMapGenerator: +- name: caddy-config + files: + - apps/caddy/Caddyfile + options: + labels: + app.kubernetes.io/name: caddy +- name: openedx-settings-lms + files:{% for file in "apps/openedx/settings/lms"|walk_templates %} + - {{ file }}{% endfor %} + options: + labels: + app.kubernetes.io/name: openedx +- name: openedx-settings-cms + files:{% for file in "apps/openedx/settings/cms"|walk_templates %} + - {{ file }}{% endfor %} + options: + labels: + app.kubernetes.io/name: openedx +- name: openedx-config + files:{% for file in "apps/openedx/config"|walk_templates %} + - {{ file }}{% endfor %} + options: + labels: + app.kubernetes.io/name: openedx +- name: redis-config + files: + - apps/redis/redis.conf + options: + labels: + app.kubernetes.io/name: redis +{{ patch("kustomization-configmapgenerator") }} + +{%- if patch("k8s-override") or patch("kustomization-patches-strategic-merge") %} +patchesStrategicMerge: +{%- if patch("k8s-override") %} +- k8s/override.yml +{%- endif %} +{{ patch("kustomization-patches-strategic-merge") }} +{%- endif %} + +{{ patch("kustomization") }} diff --git a/drydock/templates/kustomized/tutor16/defaults.yml b/drydock/templates/kustomized/tutor16/defaults.yml new file mode 100644 index 00000000..5e376dc7 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/defaults.yml @@ -0,0 +1,50 @@ +DRYDOCK_AUTO_TLS: true +DRYDOCK_CMS_LIMIT_CPU: 1 +DRYDOCK_CMS_LIMIT_MEMORY: "2Gi" +DRYDOCK_CMS_MAX_REPLICAS: 3 +DRYDOCK_CMS_MIN_REPLICAS: 3 +DRYDOCK_CMS_REQUEST_CPU: "600m" +DRYDOCK_CMS_REQUEST_MEMORY: "1Gi" +DRYDOCK_CMS_TARGET_CPU: 90 +DRYDOCK_CMS_WORKERS_MAX_REPLICAS: 3 +DRYDOCK_CMS_WORKERS_MIN_REPLICAS: 1 +DRYDOCK_CMS_WORKERS_TARGET_CPU: 90 +DRYDOCK_CMS_WORKER_LIMIT_CPU: 1 +DRYDOCK_CMS_WORKER_LIMIT_MEMORY: "2Gi" +DRYDOCK_CMS_WORKER_REQUEST_CPU: "600m" +DRYDOCK_CMS_WORKER_REQUEST_MEMORY: "1Gi" +DRYDOCK_DISABLE_JOBS: False +DRYDOCK_ENABLE_OVERRIDES: False +DRYDOCK_FLOWER: False +DRYDOCK_HPA: False +DRYDOCK_ENABLE_RESOURCE_MANAGEMENT: False +DRYDOCK_INGRESS: False +DRYDOCK_INGRESS_EXTRA_HOSTS: [] +DRYDOCK_LMS_LIMIT_CPU: 1 +DRYDOCK_LMS_LIMIT_MEMORY: "2Gi" +DRYDOCK_LMS_MAX_REPLICAS: 1 +DRYDOCK_LMS_MIN_REPLICAS: 1 +DRYDOCK_LMS_REQUEST_CPU: "600m" +DRYDOCK_LMS_REQUEST_MEMORY: "1Gi" +DRYDOCK_LMS_TARGET_CPU: 90 +DRYDOCK_LMS_WORKERS_MAX_REPLICAS: 3 +DRYDOCK_LMS_WORKERS_MIN_REPLICAS: 1 +DRYDOCK_LMS_WORKERS_TARGET_CPU: 90 +DRYDOCK_LMS_WORKER_LIMIT_CPU: 1 +DRYDOCK_LMS_WORKER_LIMIT_MEMORY: "2Gi" +DRYDOCK_LMS_WORKER_REQUEST_CPU: "600m" +DRYDOCK_LMS_WORKER_REQUEST_MEMORY: "1Gi" +DRYDOCK_NEWRELIC: False +DRYDOCK_NEWRELIC_CONFIG: "" +DRYDOCK_CUSTOM_CERTS: {} +DRYDOCK_INIT_JOBS: False +DRYDOCK_FORUM_LIMIT_MEMORY: "1Gi" +DRYDOCK_FORUM_LIMIT_CPU: 1 +DRYDOCK_FORUM_REQUEST_MEMORY: "300Mi" +DRYDOCK_FORUM_REQUEST_CPU: "200m" +DRYDOCK_FORUM_MAX_REPLICAS: 1 +DRYDOCK_FORUM_MIN_REPLICAS: 1 +DRYDOCK_FORUM_TARGET_CPU: 90 +DRYDOCK_DEBUG: False +OPENEDX_DEBUG_COOKIE: ednx_enable_debug +DRYDOCK_CMS_SSO_USER: "cms" diff --git a/drydock/templates/kustomized/tutor16/extensions/debug/deployments.yml b/drydock/templates/kustomized/tutor16/extensions/debug/deployments.yml new file mode 100644 index 00000000..179a4bf9 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/extensions/debug/deployments.yml @@ -0,0 +1,103 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: cms-debug + labels: + app.kubernetes.io/name: cms-debug + annotations: + argocd.argoproj.io/sync-wave: "5" +spec: + selector: + matchLabels: + app.kubernetes.io/name: cms-debug + template: + metadata: + labels: + app.kubernetes.io/name: cms-debug + spec: + securityContext: + runAsUser: 0 + runAsGroup: 0 + containers: + - name: cms-debug + args: [./manage.py cms runserver 0.0.0.0:8000] + command: ["/bin/bash", "-c"] + image: {{ DOCKER_IMAGE_OPENEDX }} + env: + - name: SERVICE_VARIANT + value: cms + - name: DJANGO_SETTINGS_MODULE + value: cms.envs.tutor.production + ports: + - containerPort: 8000 + volumeMounts: + - mountPath: /openedx/edx-platform/lms/envs/tutor/ + name: settings-lms + - mountPath: /openedx/edx-platform/cms/envs/tutor/ + name: settings-cms + - mountPath: /openedx/config + name: config + securityContext: + allowPrivilegeEscalation: false + stdin: true + tty: true + volumes: + - name: settings-lms + configMap: + name: openedx-settings-lms + - name: settings-cms + configMap: + name: openedx-settings-cms + - name: config + configMap: + name: openedx-config +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: lms-debug + labels: + app.kubernetes.io/name: lms-debug + annotations: + argocd.argoproj.io/sync-wave: "5" +spec: + selector: + matchLabels: + app.kubernetes.io/name: lms-debug + template: + metadata: + labels: + app.kubernetes.io/name: lms-debug + spec: + securityContext: + runAsUser: 0 + runAsGroup: 0 + containers: + - name: lms-debug + args: [./manage.py lms runserver 0.0.0.0:8000] + command: ["/bin/bash", "-c"] + image: {{ DOCKER_IMAGE_OPENEDX }} + ports: + - containerPort: 8000 + volumeMounts: + - mountPath: /openedx/edx-platform/lms/envs/tutor/ + name: settings-lms + - mountPath: /openedx/edx-platform/cms/envs/tutor/ + name: settings-cms + - mountPath: /openedx/config + name: config + securityContext: + allowPrivilegeEscalation: false + stdin: true + tty: true + volumes: + - name: settings-lms + configMap: + name: openedx-settings-lms + - name: settings-cms + configMap: + name: openedx-settings-cms + - name: config + configMap: + name: openedx-config diff --git a/drydock/templates/kustomized/tutor16/extensions/debug/ingress.yml b/drydock/templates/kustomized/tutor16/extensions/debug/ingress.yml new file mode 100644 index 00000000..1062d378 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/extensions/debug/ingress.yml @@ -0,0 +1,35 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ingress-debug + namespace: {{ K8S_NAMESPACE }} + annotations: + kubernetes.io/ingress.class: nginx + nginx.ingress.kubernetes.io/canary: "true" + nginx.ingress.kubernetes.io/canary-by-cookie: {{ OPENEDX_DEBUG_COOKIE }} + {%- if DRYDOCK_AUTO_TLS and not DRYDOCK_CUSTOM_CERTS%} + cert-manager.io/issuer: letsencrypt + {%- endif %} + argocd.argoproj.io/sync-wave: "5" +spec: + rules: + {%- for host, prefix in [(LMS_HOST, "lms"), (CMS_HOST, "cms")] %} + - host: {{ host }} + http: + paths: + - backend: + service: + name: {{prefix}}-debug + port: + number: 8000 + path: / + pathType: Prefix + {%- endfor %} + {% if DRYDOCK_AUTO_TLS -%} + tls: + - hosts: + {%- for host in [LMS_HOST, CMS_HOST] %} + - {{ host }} + {%- endfor %} + secretName: {{ K8S_NAMESPACE }}-tls + {%- endif %} diff --git a/drydock/templates/kustomized/tutor16/extensions/debug/services.yml b/drydock/templates/kustomized/tutor16/extensions/debug/services.yml new file mode 100644 index 00000000..429480b2 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/extensions/debug/services.yml @@ -0,0 +1,30 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: lms-debug + namespace: {{ K8S_NAMESPACE }} + annotations: + argocd.argoproj.io/sync-wave: "5" +spec: + ports: + - port: 8000 + protocol: TCP + targetPort: 8000 + selector: + app.kubernetes.io/name: lms-debug + type: ClusterIP +--- +apiVersion: v1 +kind: Service +metadata: + name: cms-debug + namespace: {{ K8S_NAMESPACE }} +spec: + ports: + - port: 8000 + protocol: TCP + targetPort: 8000 + selector: + app.kubernetes.io/name: cms-debug + type: ClusterIP diff --git a/drydock/templates/kustomized/tutor16/extensions/drydock-jobs/cms.yml b/drydock/templates/kustomized/tutor16/extensions/drydock-jobs/cms.yml new file mode 100644 index 00000000..747b3797 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/extensions/drydock-jobs/cms.yml @@ -0,0 +1,59 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: cms-job + labels: + app.kubernetes.io/component: job + drydock.io/target-service: cms + drydock.io/runner-service: cms + annotations: + argocd.argoproj.io/sync-wave: "3" + argocd.argoproj.io/hook: Sync + argocd.argoproj.io/hook-delete-policy: HookSucceeded +spec: + ttlSecondsAfterFinished: 100 + template: + spec: + restartPolicy: Never + containers: + - name: cms + image: {{ DOCKER_IMAGE_OPENEDX }} + command: ["/bin/sh", "-c"] + args: + - | + dockerize -wait tcp://{{ MYSQL_HOST }}:{{ MYSQL_PORT }} -timeout 20s + + echo "Loading settings $DJANGO_SETTINGS_MODULE" + + ./manage.py cms migrate + + # Fix incorrect uploaded file path + if [ -d /openedx/data/uploads/ ]; then + if [ -n "$(ls -A /openedx/data/uploads/)" ]; then + echo "Migrating CMS uploaded files to shared directory" + mv /openedx/data/uploads/* /openedx/media/ + rm -rf /openedx/data/uploads/ + fi + fi + env: + - name: SERVICE_VARIANT + value: cms + - name: DJANGO_SETTINGS_MODULE + value: cms.envs.tutor.production + volumeMounts: + - mountPath: /openedx/edx-platform/lms/envs/tutor/ + name: settings-lms + - mountPath: /openedx/edx-platform/cms/envs/tutor/ + name: settings-cms + - mountPath: /openedx/config + name: config + volumes: + - name: settings-lms + configMap: + name: openedx-settings-lms + - name: settings-cms + configMap: + name: openedx-settings-cms + - name: config + configMap: + name: openedx-config diff --git a/drydock/templates/kustomized/tutor16/extensions/drydock-jobs/extra-jobs.yml b/drydock/templates/kustomized/tutor16/extensions/drydock-jobs/extra-jobs.yml new file mode 100644 index 00000000..258bc240 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/extensions/drydock-jobs/extra-jobs.yml @@ -0,0 +1 @@ +{{ patch("drydock-jobs")}} diff --git a/drydock/templates/kustomized/tutor16/extensions/drydock-jobs/forum.yml b/drydock/templates/kustomized/tutor16/extensions/drydock-jobs/forum.yml new file mode 100644 index 00000000..24029064 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/extensions/drydock-jobs/forum.yml @@ -0,0 +1,46 @@ +{%- if FORUM_DOCKER_IMAGE is defined %} +apiVersion: batch/v1 +kind: Job +metadata: + name: forum-job + labels: + app.kubernetes.io/component: job + drydock.io/target-service: forum + drydock.io/runner-service: forum + annotations: + argocd.argoproj.io/sync-wave: "2" + argocd.argoproj.io/hook: Sync + argocd.argoproj.io/hook-delete-policy: HookSucceeded +spec: + ttlSecondsAfterFinished: 100 + template: + spec: + restartPolicy: Never + containers: + - name: forum + image: {{ FORUM_DOCKER_IMAGE }} + args: + - sh + - -e + - -c + - 'bundle exec rake search:initialize + + bundle exec rake search:rebuild_indices' + env: + - name: SEARCH_SERVER + value: "{{ ELASTICSEARCH_SCHEME }}://{{ ELASTICSEARCH_HOST }}:{{ ELASTICSEARCH_PORT }}" + - name: MONGODB_AUTH + value: "{% if MONGODB_USERNAME and MONGODB_PASSWORD %}{{ MONGODB_USERNAME}}:{{ MONGODB_PASSWORD }}@{% endif %}" + - name: MONGODB_HOST + value: "{{ MONGODB_HOST }}" + - name: MONGODB_PORT + value: "{{ MONGODB_PORT }}" + - name: MONGODB_DATABASE + value: "{{ FORUM_MONGODB_DATABASE }}" + - name: MONGOID_USE_SSL + value: "{{ 'true' if MONGODB_USE_SSL else 'false' }}" + - name: MONGOID_AUTH_SOURCE + value: "{{MONGODB_AUTH_SOURCE}}" + - name: MONGOID_AUTH_MECH + value: "{{ MONGODB_AUTH_MECHANISM|auth_mech_as_ruby }}" +{%- endif %} diff --git a/drydock/templates/kustomized/tutor16/extensions/drydock-jobs/lms.yml b/drydock/templates/kustomized/tutor16/extensions/drydock-jobs/lms.yml new file mode 100644 index 00000000..e3f955a1 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/extensions/drydock-jobs/lms.yml @@ -0,0 +1,80 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: lms-job + labels: + app.kubernetes.io/component: job + drydock.io/target-service: lms + drydock.io/runner-service: lms + annotations: + argocd.argoproj.io/sync-wave: "2" + argocd.argoproj.io/hook: Sync + argocd.argoproj.io/hook-delete-policy: HookSucceeded +spec: + ttlSecondsAfterFinished: 100 + template: + spec: + restartPolicy: Never + containers: + - name: lms + image: {{ DOCKER_IMAGE_OPENEDX }} + command: ["/bin/sh", "-c"] + args: + - | + dockerize -wait tcp://{{ MYSQL_HOST }}:{{ MYSQL_PORT }} -timeout 20s + {%- if MONGODB_HOST.startswith("mongodb+srv://") %} + echo "MongoDB is using SRV records, so we cannot wait for it to be ready" + {%- else %} + dockerize -wait tcp://{{ MONGODB_HOST }}:{{ MONGODB_PORT }} -timeout 20s + {%- endif %} + + echo "Loading settings $DJANGO_SETTINGS_MODULE" + + ./manage.py lms migrate + + # Create oauth2 apps for CMS SSO + # https://github.com/openedx/edx-platform/blob/master/docs/guides/studio_oauth.rst + ./manage.py lms manage_user {{ DRYDOCK_CMS_SSO_USER }} {{ DRYDOCK_CMS_SSO_USER }}@openedx --unusable-password + ./manage.py lms create_dot_application \ + --grant-type authorization-code \ + --redirect-uris "{% if ENABLE_HTTPS %}https{% else %}http{% endif %}://{{ CMS_HOST }}/complete/edx-oauth2/" \ + --client-id {{ CMS_OAUTH2_KEY_SSO }} \ + --client-secret {{ CMS_OAUTH2_SECRET }} \ + --scopes user_id \ + --skip-authorization \ + --update cms-sso {{ DRYDOCK_CMS_SSO_USER }} + + # Fix incorrect uploaded file path + if [ -d /openedx/data/uploads/ ]; then + if [ -n "$(ls -A /openedx/data/uploads/)" ]; then + echo "Migrating LMS uploaded files to shared directory" + mv /openedx/data/uploads/* /openedx/media/ + rm -rf /openedx/data/uploads/ + fi + fi + + # Create waffle switches to enable some features, if they have not been explicitly defined before + # Completion tracking: add green ticks to every completed unit + (./manage.py lms waffle_switch --list | grep completion.enable_completion_tracking) || ./manage.py lms waffle_switch --create completion.enable_completion_tracking on + env: + - name: SERVICE_VARIANT + value: lms + - name: DJANGO_SETTINGS_MODULE + value: lms.envs.tutor.production + volumeMounts: + - mountPath: /openedx/edx-platform/lms/envs/tutor/ + name: settings-lms + - mountPath: /openedx/edx-platform/cms/envs/tutor/ + name: settings-cms + - mountPath: /openedx/config + name: config + volumes: + - name: settings-lms + configMap: + name: openedx-settings-lms + - name: settings-cms + configMap: + name: openedx-settings-cms + - name: config + configMap: + name: openedx-config diff --git a/drydock/templates/kustomized/tutor16/extensions/drydock-jobs/minio.yml b/drydock/templates/kustomized/tutor16/extensions/drydock-jobs/minio.yml new file mode 100644 index 00000000..1a95c3b8 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/extensions/drydock-jobs/minio.yml @@ -0,0 +1,35 @@ +{%- if MINIO_DOCKER_IMAGE is defined %} +apiVersion: batch/v1 +kind: Job +metadata: + name: minio-job + labels: + app.kubernetes.io/component: job + drydock.io/target-service: minio + drydock.io/runner-service: minio + annotations: + argocd.argoproj.io/sync-wave: "1" + argocd.argoproj.io/hook: Sync + argocd.argoproj.io/hook-delete-policy: HookSucceeded +spec: + ttlSecondsAfterFinished: 100 + template: + spec: + restartPolicy: Never + containers: + - name: minio + image: {{ MINIO_MC_DOCKER_IMAGE }} + command: ["/bin/sh", "-c"] + args: + - | + mc config host add minio http://minio:9000 {{ OPENEDX_AWS_ACCESS_KEY }} {{ OPENEDX_AWS_SECRET_ACCESS_KEY }} --api s3v4 + mc mb --ignore-existing minio/{{ MINIO_BUCKET_NAME }} minio/{{ MINIO_FILE_UPLOAD_BUCKET_NAME }} minio/{{ MINIO_VIDEO_UPLOAD_BUCKET_NAME }} + + # Make common file upload bucket public (e.g: for forum image upload) + mc policy set public minio/{{ MINIO_BUCKET_NAME }} + env: + - name: MINIO_ROOT_USER + value: "{{ OPENEDX_AWS_ACCESS_KEY }}" + - name: MINIO_ROOT_PASSWORD + value: "{{ OPENEDX_AWS_SECRET_ACCESS_KEY }}" +{%- endif %} \ No newline at end of file diff --git a/drydock/templates/kustomized/tutor16/extensions/drydock-jobs/mysql.yml b/drydock/templates/kustomized/tutor16/extensions/drydock-jobs/mysql.yml new file mode 100644 index 00000000..ab4e2c63 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/extensions/drydock-jobs/mysql.yml @@ -0,0 +1,44 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: mysql-job + labels: + app.kubernetes.io/component: job + drydock.io/target-service: mysql + drydock.io/runner-service: mysql + annotations: + argocd.argoproj.io/sync-wave: "1" + argocd.argoproj.io/hook: Sync + argocd.argoproj.io/hook-delete-policy: HookSucceeded +spec: + ttlSecondsAfterFinished: 100 + template: + spec: + restartPolicy: Never + containers: + - name: mysql + image: {{ DOCKER_IMAGE_MYSQL }} + command: ["/bin/sh", "-c"] + args: + - | + echo "Initialising MySQL..." + mysql_connection_max_attempts=10 + mysql_connection_attempt=0 + until mysql -u {{ MYSQL_ROOT_USERNAME }} --password="{{ MYSQL_ROOT_PASSWORD }}" --host "{{ MYSQL_HOST }}" --port {{ MYSQL_PORT }} -e 'exit' + do + mysql_connection_attempt=$(expr $mysql_connection_attempt + 1) + echo " [$mysql_connection_attempt/$mysql_connection_max_attempts] Waiting for MySQL service (this may take a while)..." + if [ $mysql_connection_attempt -eq $mysql_connection_max_attempts ] + then + echo "MySQL initialisation error" 1>&2 + exit 1 + fi + sleep 10 + done + echo "MySQL is up and running" + + # edx-platform database + mysql -u {{ MYSQL_ROOT_USERNAME }} --password="{{ MYSQL_ROOT_PASSWORD }}" --host "{{ MYSQL_HOST }}" --port {{ MYSQL_PORT }} -e "CREATE DATABASE IF NOT EXISTS {{ OPENEDX_MYSQL_DATABASE }};" + mysql -u {{ MYSQL_ROOT_USERNAME }} --password="{{ MYSQL_ROOT_PASSWORD }}" --host "{{ MYSQL_HOST }}" --port {{ MYSQL_PORT }} -e "CREATE USER IF NOT EXISTS '{{ OPENEDX_MYSQL_USERNAME }}';" + mysql -u {{ MYSQL_ROOT_USERNAME }} --password="{{ MYSQL_ROOT_PASSWORD }}" --host "{{ MYSQL_HOST }}" --port {{ MYSQL_PORT }} -e "ALTER USER '{{ OPENEDX_MYSQL_USERNAME }}'@'%' IDENTIFIED BY '{{ OPENEDX_MYSQL_PASSWORD }}';" + mysql -u {{ MYSQL_ROOT_USERNAME }} --password="{{ MYSQL_ROOT_PASSWORD }}" --host "{{ MYSQL_HOST }}" --port {{ MYSQL_PORT }} -e "GRANT ALL ON {{ OPENEDX_MYSQL_DATABASE }}.* TO '{{ OPENEDX_MYSQL_USERNAME }}'@'%';" diff --git a/drydock/templates/kustomized/tutor16/extensions/drydock-jobs/notes.yml b/drydock/templates/kustomized/tutor16/extensions/drydock-jobs/notes.yml new file mode 100644 index 00000000..7f6d1b3c --- /dev/null +++ b/drydock/templates/kustomized/tutor16/extensions/drydock-jobs/notes.yml @@ -0,0 +1,123 @@ +{%- if NOTES_DOCKER_IMAGE is defined %} +apiVersion: batch/v1 +kind: Job +metadata: + name: notes-job-mysql + labels: + app.kubernetes.io/component: job + drydock.io/target-service: notes + drydock.io/runner-service: mysql + annotations: + argocd.argoproj.io/sync-wave: "1" + argocd.argoproj.io/hook: Sync + argocd.argoproj.io/hook-delete-policy: HookSucceeded +spec: + ttlSecondsAfterFinished: 100 + template: + spec: + restartPolicy: Never + containers: + - name: notes + image: {{ DOCKER_IMAGE_MYSQL }} + command: ["/bin/sh", "-c"] + args: + - | + mysql -u {{ MYSQL_ROOT_USERNAME }} --password="{{ MYSQL_ROOT_PASSWORD }}" --host "{{ MYSQL_HOST }}" --port {{ MYSQL_PORT }} -e 'CREATE DATABASE IF NOT EXISTS {{ NOTES_MYSQL_DATABASE }};' + mysql -u {{ MYSQL_ROOT_USERNAME }} --password="{{ MYSQL_ROOT_PASSWORD }}" --host "{{ MYSQL_HOST }}" --port {{ MYSQL_PORT }} -e "CREATE USER IF NOT EXISTS '{{ NOTES_MYSQL_USERNAME }}';" + mysql -u {{ MYSQL_ROOT_USERNAME }} --password="{{ MYSQL_ROOT_PASSWORD }}" --host "{{ MYSQL_HOST }}" --port {{ MYSQL_PORT }} -e "ALTER USER '{{ NOTES_MYSQL_USERNAME }}'@'%' IDENTIFIED BY '{{ NOTES_MYSQL_PASSWORD }}';" + mysql -u {{ MYSQL_ROOT_USERNAME }} --password="{{ MYSQL_ROOT_PASSWORD }}" --host "{{ MYSQL_HOST }}" --port {{ MYSQL_PORT }} -e "GRANT ALL ON {{ NOTES_MYSQL_DATABASE }}.* TO '{{ NOTES_MYSQL_USERNAME }}'@'%';" +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: notes-job + labels: + app.kubernetes.io/component: job + drydock.io/target-service: notes + drydock.io/runner-service: notes + annotations: + argocd.argoproj.io/sync-wave: "3" + argocd.argoproj.io/hook: Sync + argocd.argoproj.io/hook-delete-policy: HookSucceeded +spec: + ttlSecondsAfterFinished: 100 + template: + spec: + restartPolicy: Never + containers: + - name: notes + image: {{ NOTES_DOCKER_IMAGE }} + command: ["/bin/sh", "-c"] + args: [./manage.py migrate] + env: + - name: DJANGO_SETTINGS_MODULE + value: notesserver.settings.tutor + volumeMounts: + - mountPath: /app/edx-notes-api/notesserver/settings/tutor.py + name: settings + subPath: tutor.py + volumes: + - name: settings + configMap: + name: notes-settings +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: notes-job-lms + labels: + app.kubernetes.io/component: job + drydock.io/target-service: notes + drydock.io/runner-service: lms + annotations: + argocd.argoproj.io/sync-wave: "3" + argocd.argoproj.io/hook: Sync + argocd.argoproj.io/hook-delete-policy: HookSucceeded +spec: + ttlSecondsAfterFinished: 100 + template: + spec: + restartPolicy: Never + containers: + - name: notes + image: {{ DOCKER_IMAGE_OPENEDX }} + command: ["/bin/sh", "-c"] + args: + - | + # Modify users created an incorrect email and that might clash with the newly created users + ./manage.py lms shell -c \ + "from django.contrib.auth import get_user_model;\ + get_user_model().objects.filter(username='notes').exclude(email='notes@openedx').update(email='notes@openedx')" + + ./manage.py lms manage_user notes notes@openedx --staff --superuser + ./manage.py lms create_dot_application \ + notes \ + notes \ + --redirect-uris "http://notes:8000" \ + --skip-authorization \ + --client-id notes \ + --client-secret "{{ NOTES_OAUTH2_SECRET }}" \ + --update + env: + - name: SERVICE_VARIANT + value: lms + - name: DJANGO_SETTINGS_MODULE + value: lms.envs.tutor.production + volumeMounts: + - mountPath: /openedx/edx-platform/lms/envs/tutor/ + name: settings-lms + - mountPath: /openedx/edx-platform/cms/envs/tutor/ + name: settings-cms + - mountPath: /openedx/config + name: config + volumes: + - name: settings-lms + configMap: + name: openedx-settings-lms + - name: settings-cms + configMap: + name: openedx-settings-cms + - name: config + configMap: + name: openedx-config +{%- endif %} diff --git a/drydock/templates/kustomized/tutor16/extensions/flowers.yml b/drydock/templates/kustomized/tutor16/extensions/flowers.yml new file mode 100644 index 00000000..13172355 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/extensions/flowers.yml @@ -0,0 +1,25 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: flower-edxapp + labels: + app.kubernetes.io/name: flower-edxapp +spec: + selector: + matchLabels: + app.kubernetes.io/name: flower-edxapp + template: + metadata: + labels: + app.kubernetes.io/name: flower-edxapp + spec: + containers: + - name: flower-edxapp + image: docker.io/mher/flower:0.9.5 + ports: + - containerPort: 5555 + env: + - name: CELERY_BROKER_URL + value: redis://{{ REDIS_USERNAME }}:{{ REDIS_PASSWORD }}@{{ REDIS_HOST }}:{{ REDIS_PORT }}/{{ OPENEDX_CELERY_REDIS_DB }} + - name: FLOWER_PORT + value: "5555" diff --git a/drydock/templates/kustomized/tutor16/extensions/hpa.yml b/drydock/templates/kustomized/tutor16/extensions/hpa.yml new file mode 100644 index 00000000..0500d3db --- /dev/null +++ b/drydock/templates/kustomized/tutor16/extensions/hpa.yml @@ -0,0 +1,82 @@ +--- +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: lms + namespace: {{ K8S_NAMESPACE }} + annotations: + argocd.argoproj.io/sync-wave: "5" +spec: + maxReplicas: {{ DRYDOCK_LMS_MAX_REPLICAS }} + minReplicas: {{ DRYDOCK_LMS_MIN_REPLICAS }} + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: lms + targetCPUUtilizationPercentage: {{ DRYDOCK_LMS_TARGET_CPU }} +--- +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: cms + namespace: {{ K8S_NAMESPACE }} + annotations: + argocd.argoproj.io/sync-wave: "5" +spec: + maxReplicas: {{ DRYDOCK_CMS_MAX_REPLICAS }} + minReplicas: {{ DRYDOCK_CMS_MIN_REPLICAS }} + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: cms + targetCPUUtilizationPercentage: {{ DRYDOCK_CMS_TARGET_CPU }} +--- +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: lms-worker + namespace: {{ K8S_NAMESPACE }} + annotations: + argocd.argoproj.io/sync-wave: "5" +spec: + maxReplicas: {{ DRYDOCK_LMS_WORKERS_MAX_REPLICAS }} + minReplicas: {{ DRYDOCK_LMS_WORKERS_MIN_REPLICAS }} + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: lms-worker + targetCPUUtilizationPercentage: {{ DRYDOCK_LMS_WORKERS_TARGET_CPU }} +--- +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: cms-worker + namespace: {{ K8S_NAMESPACE }} + annotations: + argocd.argoproj.io/sync-wave: "5" +spec: + maxReplicas: {{ DRYDOCK_CMS_WORKERS_MAX_REPLICAS }} + minReplicas: {{ DRYDOCK_CMS_WORKERS_MIN_REPLICAS }} + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: cms-worker + targetCPUUtilizationPercentage: {{ DRYDOCK_CMS_WORKERS_TARGET_CPU }} +{%- if FORUM_DOCKER_IMAGE is defined %} +--- +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: forum + namespace: {{ K8S_NAMESPACE }} + annotations: + argocd.argoproj.io/sync-wave: "5" +spec: + maxReplicas: {{ DRYDOCK_FORUM_MAX_REPLICAS }} + minReplicas: {{ DRYDOCK_FORUM_MIN_REPLICAS }} + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: forum + targetCPUUtilizationPercentage: {{ DRYDOCK_FORUM_TARGET_CPU }} +{%- endif %} diff --git a/drydock/templates/kustomized/tutor16/extensions/ingress.yml b/drydock/templates/kustomized/tutor16/extensions/ingress.yml new file mode 100644 index 00000000..08958e1e --- /dev/null +++ b/drydock/templates/kustomized/tutor16/extensions/ingress.yml @@ -0,0 +1,85 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ingress + namespace: {{ K8S_NAMESPACE }} + annotations: + kubernetes.io/ingress.class: nginx + {%- if DRYDOCK_AUTO_TLS and not DRYDOCK_CUSTOM_CERTS%} + cert-manager.io/issuer: letsencrypt + {%- endif %} +spec: + rules: + {%- for host in [LMS_HOST, PREVIEW_LMS_HOST, CMS_HOST] %} + - host: {{ host }} + http: + paths: + - pathType: Prefix + path: "/" + backend: + service: + name: caddy + port: + number: 80 + {%- endfor %} + {%- for host in DRYDOCK_INGRESS_EXTRA_HOSTS %} + - host: {{ host }} + http: + paths: + - pathType: Prefix + path: "/" + backend: + service: + name: caddy + port: + number: 80 + {%- endfor %} + {% if DRYDOCK_AUTO_TLS or DRYDOCK_CUSTOM_CERTS -%} + tls: + - hosts: + {%- for host in [LMS_HOST, PREVIEW_LMS_HOST, CMS_HOST] %} + - {{ host }} + {%- endfor %} + {%- for host in DRYDOCK_INGRESS_EXTRA_HOSTS %} + - {{ host }} + {%- endfor %} + {% if DRYDOCK_CUSTOM_CERTS -%} + secretName: {{ DRYDOCK_CUSTOM_CERTS["secret_name"]|default("custom-tls-certs") }} + {% else -%} + secretName: {{ K8S_NAMESPACE }}-tls + {%- endif %} + {%- endif %} +{% if DRYDOCK_CUSTOM_CERTS -%} +--- +apiVersion: v1 +kind: Secret +type: kubernetes.io/tls +metadata: + name: {{ DRYDOCK_CUSTOM_CERTS["secret_name"]|default("custom-tls-certs") }} + namespace: {{ K8S_NAMESPACE }} +data: + tls.crt: {{ DRYDOCK_CUSTOM_CERTS["crt"] }} + tls.key: {{ DRYDOCK_CUSTOM_CERTS["key"] }} +{% elif DRYDOCK_AUTO_TLS %} +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: letsencrypt + namespace: {{ K8S_NAMESPACE }} + labels: + app.kubernetes.io/name: letsencrypt +spec: + acme: + # Let's Encrypt will use this to contact you about expiring + # certificates, and issues related to your account. + email: {{ DRYDOCK_LETSENCRYPT_EMAIL|default(CONTACT_EMAIL) }} + # Secret resource that will be used to store the account's private key. + privateKeySecretRef: + name: {{ K8S_NAMESPACE }}-letsencrypt-account-key + server: https://acme-v02.api.letsencrypt.org/directory + solvers: + - http01: + ingress: + class: nginx +{%- endif %} diff --git a/drydock/templates/kustomized/tutor16/extensions/kustomization.yml b/drydock/templates/kustomized/tutor16/extensions/kustomization.yml new file mode 100644 index 00000000..b24c0db2 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/extensions/kustomization.yml @@ -0,0 +1,52 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +{% if DRYDOCK_FLOWERS|default(false) -%} +- flowers.yml +{% endif -%} +{% if DRYDOCK_INGRESS|default(false) -%} +- ingress.yml +{% endif -%} +{% if DRYDOCK_HPA|default(false) and DRYDOCK_ENABLE_OVERRIDES|default(false) -%} +- hpa.yml +{% endif -%} +{%- if DRYDOCK_INIT_JOBS %} +- drydock-jobs/mysql.yml +- drydock-jobs/lms.yml +- drydock-jobs/cms.yml +- drydock-jobs/minio.yml +- drydock-jobs/forum.yml +- drydock-jobs/notes.yml +- drydock-jobs/extra-jobs.yml +{%- endif %} +{%- if DRYDOCK_DEBUG %} +- debug/deployments.yml +- debug/services.yml +- debug/ingress.yml +{%- endif %} +- multipurpose-jobs.yml + +{% if DRYDOCK_NEWRELIC -%} +configMapGenerator: +- name: newrelic-cm + files: + - newrelic.ini + options: + labels: + app.kubernetes.io/name: openedx +{% endif -%} + +{% if not RUN_MYSQL -%} +# MySQL job deletion based on the thread in https://github.com/kubernetes-sigs/kustomize/issues/4526 +patches: +- target: + kind: Job + labelSelector: drydock.io/runner-service=mysql + patch: |- + apiVersion: batch/v1 + kind: Job + metadata: + name: does-not-matter + $patch: delete +{%- endif %} diff --git a/drydock/templates/kustomized/tutor16/extensions/multipurpose-jobs.yml b/drydock/templates/kustomized/tutor16/extensions/multipurpose-jobs.yml new file mode 100644 index 00000000..cb6f96b6 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/extensions/multipurpose-jobs.yml @@ -0,0 +1 @@ +{{ patch("drydock-multipurpose-jobs") }} diff --git a/drydock/templates/kustomized/tutor16/extensions/newrelic.ini b/drydock/templates/kustomized/tutor16/extensions/newrelic.ini new file mode 100644 index 00000000..7fd9998c --- /dev/null +++ b/drydock/templates/kustomized/tutor16/extensions/newrelic.ini @@ -0,0 +1 @@ +{{ DRYDOCK_NEWRELIC_CONFIG }} diff --git a/drydock/templates/kustomized/tutor16/extensions/overrides.yml b/drydock/templates/kustomized/tutor16/extensions/overrides.yml new file mode 100644 index 00000000..a9da35e3 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/extensions/overrides.yml @@ -0,0 +1,132 @@ +{%- if DRYDOCK_ENABLE_RESOURCE_MANAGEMENT -%} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: lms +spec: + template: + spec: + containers: + - name: lms + resources: + limits: + cpu: "{{ DRYDOCK_LMS_LIMIT_CPU }}" + memory: "{{ DRYDOCK_LMS_LIMIT_MEMORY }}" + requests: + cpu: "{{ DRYDOCK_LMS_REQUEST_CPU }}" + memory: "{{ DRYDOCK_LMS_REQUEST_MEMORY }}" +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: cms +spec: + template: + spec: + containers: + - name: cms + resources: + limits: + cpu: "{{ DRYDOCK_CMS_LIMIT_CPU }}" + memory: "{{ DRYDOCK_CMS_LIMIT_MEMORY }}" + requests: + cpu: "{{ DRYDOCK_CMS_REQUEST_CPU }}" + memory: "{{ DRYDOCK_CMS_REQUEST_MEMORY }}" +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: lms-worker +spec: + template: + spec: + containers: + - name: lms-worker + resources: + limits: + cpu: "{{ DRYDOCK_LMS_WORKER_LIMIT_CPU }}" + memory: "{{ DRYDOCK_LMS_WORKER_LIMIT_MEMORY }}" + requests: + cpu: "{{ DRYDOCK_LMS_WORKER_REQUEST_CPU }}" + memory: "{{ DRYDOCK_LMS_WORKER_REQUEST_MEMORY }}" +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: cms-worker +spec: + template: + spec: + containers: + - name: cms-worker + resources: + limits: + cpu: "{{ DRYDOCK_CMS_WORKER_LIMIT_CPU }}" + memory: "{{ DRYDOCK_CMS_WORKER_LIMIT_MEMORY }}" + requests: + cpu: "{{ DRYDOCK_CMS_WORKER_REQUEST_CPU }}" + memory: "{{ DRYDOCK_CMS_WORKER_REQUEST_MEMORY }}" +{%- if FORUM_DOCKER_IMAGE is defined %} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: forum +spec: + template: + spec: + containers: + - name: forum + resources: + limits: + cpu: "{{ DRYDOCK_FORUM_LIMIT_CPU }}" + memory: "{{ DRYDOCK_FORUM_LIMIT_MEMORY }}" + requests: + cpu: "{{ DRYDOCK_FORUM_REQUEST_CPU }}" + memory: "{{ DRYDOCK_FORUM_REQUEST_MEMORY }}" +{%- endif %} +--- +{%- endif %} +{%- if DRYDOCK_NEWRELIC %} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: lms +spec: + template: + spec: + containers: + - name: lms + args: + - | + newrelic-admin run-program uwsgi \ + --static-map /static=/openedx/staticfiles/ \ + --static-map /media=/openedx/media/ \ + --http 0.0.0.0:8000 --thunder-lock \ + --single-interpreter --enable-threads \ + --processes=${UWSGI_WORKERS:-2} \ + --buffer-size=8192 \ + --wsgi-file $SERVICE_VARIANT/wsgi.py + command: ["/bin/bash", "-c"] + env: + - name: NEW_RELIC_CONFIG_FILE + value: /openedx/newrelic/newrelic.ini + volumeMounts: + - mountPath: /openedx/newrelic/ + name: newrelic-ini + volumes: + - configMap: + name: newrelic-cm + name: newrelic-ini +--- +{%- endif %} +{%- if FORUM_DOCKER_IMAGE is defined %} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: forum + annotations: + argocd.argoproj.io/sync-wave: "4" +--- +{%- endif %} +{{ patch("drydock-overrides") }} diff --git a/drydock/templates/kustomized/tutor16/kustomization.yml b/drydock/templates/kustomized/tutor16/kustomization.yml new file mode 100644 index 00000000..8e105a67 --- /dev/null +++ b/drydock/templates/kustomized/tutor16/kustomization.yml @@ -0,0 +1,17 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: {{ K8S_NAMESPACE }} + +resources: +- base +- extensions +{{ patch("drydock-kustomization-resources") }} + +{%- if DRYDOCK_ENABLE_OVERRIDES %} +patchesStrategicMerge: +{%- if DRYDOCK_ENABLE_RESOURCE_MANAGEMENT or patch("drydock-overrides") or DRYDOCK_NEWRELIC %} +- extensions/overrides.yml +{%- endif %} +{{ patch("drydock-kustomization-patches") }} +{%- endif %}