From d568237eb0ad2e41d24a8d1a5ea4fbd26b618295 Mon Sep 17 00:00:00 2001 From: Henrry Pulgarin <39854568+Henrrypg@users.noreply.github.com> Date: Fri, 15 Mar 2024 16:13:45 -0500 Subject: [PATCH] feat: add autogenerated init jobs olive (#79) --- README.rst | 24 +++ drydock/hooks.py | 15 ++ drydock/patches/k8s-jobs | 15 ++ drydock/patches/kustomization | 17 +- drydock/patches/kustomization-resources | 8 +- drydock/plugin.py | 153 ++++++++++++++++-- .../drydock/k8s/debug/deployments.yml | 4 + .../templates/drydock/k8s/debug/ingress.yml | 1 + .../drydock/k8s/drydock-jobs/cms.yml | 62 ------- .../drydock/k8s/drydock-jobs/forum.yml | 46 ------ .../drydock/k8s/drydock-jobs/lms.yml | 83 ---------- .../drydock/k8s/drydock-jobs/minio.yml | 38 ----- .../drydock/k8s/drydock-jobs/mongodb.yml | 52 ------ .../drydock/k8s/drydock-jobs/mysql.yml | 47 ------ .../drydock/k8s/drydock-jobs/notes.yml | 132 --------------- .../templates/drydock/k8s/forum-overrides.yml | 0 drydock/templates/drydock/k8s/jobs.yml | 113 +++++++++++++ .../templates/drydock/k8s/lifecycle/cms.yml | 2 + .../templates/drydock/k8s/lifecycle/lms.yml | 2 + .../drydock/k8s/patches/hpa-sync-wave.yml | 8 + .../drydock/k8s/patches/sync-wave-4.yml | 6 - .../drydock/k8s/patches/sync-wave-5.yml | 6 - drydock/templates/drydock/task/mongodb/init | 25 +++ 23 files changed, 357 insertions(+), 502 deletions(-) create mode 100644 drydock/hooks.py create mode 100644 drydock/patches/k8s-jobs delete mode 100644 drydock/templates/drydock/k8s/drydock-jobs/cms.yml delete mode 100644 drydock/templates/drydock/k8s/drydock-jobs/forum.yml delete mode 100644 drydock/templates/drydock/k8s/drydock-jobs/lms.yml delete mode 100644 drydock/templates/drydock/k8s/drydock-jobs/minio.yml delete mode 100644 drydock/templates/drydock/k8s/drydock-jobs/mongodb.yml delete mode 100644 drydock/templates/drydock/k8s/drydock-jobs/mysql.yml delete mode 100644 drydock/templates/drydock/k8s/drydock-jobs/notes.yml delete mode 100644 drydock/templates/drydock/k8s/forum-overrides.yml create mode 100644 drydock/templates/drydock/k8s/jobs.yml create mode 100644 drydock/templates/drydock/k8s/patches/hpa-sync-wave.yml delete mode 100644 drydock/templates/drydock/k8s/patches/sync-wave-4.yml delete mode 100644 drydock/templates/drydock/k8s/patches/sync-wave-5.yml create mode 100644 drydock/templates/drydock/task/mongodb/init diff --git a/README.rst b/README.rst index 6d8b3b2e..74db8558 100644 --- a/README.rst +++ b/README.rst @@ -57,6 +57,30 @@ The following configuration options are available: - `DRYDOCK_ENABLE_SENTRY` : Whether to enable sentry. Defaults to `true`. - `DRYDOCK_SENTRY_DSN` : The sentry DSN. Defaults to `""`. - `DRYDOCK_POD_LIFECYCLE` : Whether to enable pod lifecycle. Defaults to `true`. +- `DRYDOCK_MIGRATE_FROM`: it allows defining the version of the OpenedX platform we are migrating from. It accepts the integer value mapping the origin release, for instance, `13`(maple) or `14`(nutmeg). When this variable is set, a group of `release-specific upgrade jobs` are added to the Kubernetes manifests. These jobs are applied to the cluster in a suitable order (thanks to the GitOps implementation with ArgoCD + sync waves) to guarantee the correct behavior of the platform in the new version. This brings the `tutor k8s upgrade `_ command to the GitOps pattern. The release-specific upgrade jobs are supported from release `13`(maple). Defaults to `0` (which disables release-specific upgrade jobs) + +.. note:: + You also need to set `DRYDOCK_INIT_JOBS` to `true` to enable the release-specific upgrade jobs in the case of a platform migration. + +Job generation +-------------- + +Tutor doesn't generate manifest files for the initialization jobs, in consequence we can't use GitOps tools like ArgoCD to deploy the initialization jobs. + +We had been using a static definition of the initialization jobs, but now we are using the `Tutor filters `_ to generate the kubernetes definition of the initialization jobs. This is a big improvement because now we can add new initialization jobs without modifying the Drydock code. The jobs are taken from `COMMANDS_PRE_INIT`, `COMMANDS_INIT` and `CLI_DO_INIT_TASKS` Filters. + +ArgoCD Sync Waves Support +----------------------- + +`Tutor filter `_ **SYNC_WAVES_ORDER** was added to allow define `ArgoCD Sync Waves `_ order and apply to the kubernetes resources through **get_sync_waves_for_resource** function. + +We are defined by defult the following order: +- `All kubernetes resources` (except the ones that are defined in the next waves) +- `Initialization Jobs` +- `Upgrade Jobs`: When **DRYDOCK_MIGRATE_FROM** is set, over the Sync Wave 50 +- `CMS and LMS Deployments`: When **DRYDOCK_POD_LIFECYCLE** is active, over the Sync Wave 100 +- `Debug Resources`: When **DRYDOCK_DEBUG** active, over the Sync Wave 100 +- `Horizontal Pod Autoscalers`: When active, over the Sync Wave 150 Rationale --------- diff --git a/drydock/hooks.py b/drydock/hooks.py new file mode 100644 index 00000000..2a1aea24 --- /dev/null +++ b/drydock/hooks.py @@ -0,0 +1,15 @@ +""" +These hooks are stored in a separate module. If they were included in plugin.py, then +the drydock hooks would be created in the context of some other plugin that imports +them. +""" + +from __future__ import annotations + +import typing as t + +from tutor.core.hooks import Filter, filters + +SYNC_WAVES_ORDER_ATTRS_TYPE = t.Dict[str, int] + +SYNC_WAVES_ORDER: Filter[SYNC_WAVES_ORDER_ATTRS_TYPE, []] = filters.get("sync_waves_order") diff --git a/drydock/patches/k8s-jobs b/drydock/patches/k8s-jobs new file mode 100644 index 00000000..dc33a214 --- /dev/null +++ b/drydock/patches/k8s-jobs @@ -0,0 +1,15 @@ +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: mongodb-job + labels: + app.kubernetes.io/component: job +spec: + ttlSecondsAfterFinished: 100 + template: + spec: + restartPolicy: Never + containers: + - name: mongodb + image: {{ DOCKER_IMAGE_MONGODB }} diff --git a/drydock/patches/kustomization b/drydock/patches/kustomization index f5c4bddf..7d1acfc6 100644 --- a/drydock/patches/kustomization +++ b/drydock/patches/kustomization @@ -13,21 +13,10 @@ patches: kind: Job labelSelector: app.kubernetes.io/component=job path: plugins/drydock/k8s/patches/tutor-jobs.yml -# Patch the sync waves -- target: - kind: Deployment - name: "lms|cms|lms-worker|cms-worker|forum" - path: plugins/drydock/k8s/patches/sync-wave-4.yml -{%- if DRYDOCK_DEBUG is defined and DRYDOCK_DEBUG %} -- target: - kind: Deployment|Ingress|Service - name: "cms-debug|lms-debug|ingress-debug" - path: plugins/drydock/k8s/patches/sync-wave-5.yml -{%- endif %} -- target: - kind: HorizontalPodAutoscaler - path: plugins/drydock/k8s/patches/sync-wave-5.yml {% if DRYDOCK_ENABLE_CELERY_TUNING %} - path: plugins/drydock/k8s/celery/cms-worker.yml - path: plugins/drydock/k8s/celery/lms-worker.yml {% endif -%} +- target: + kind: HorizontalPodAutoscaler + path: plugins/drydock/k8s/patches/hpa-sync-wave.yml diff --git a/drydock/patches/kustomization-resources b/drydock/patches/kustomization-resources index f1cccaf1..8040c255 100644 --- a/drydock/patches/kustomization-resources +++ b/drydock/patches/kustomization-resources @@ -1,12 +1,6 @@ - plugins/drydock/k8s/multipurpose-jobs.yml {%- if DRYDOCK_INIT_JOBS %} -- plugins/drydock/k8s/drydock-jobs/mysql.yml -- plugins/drydock/k8s/drydock-jobs/mongodb.yml -- plugins/drydock/k8s/drydock-jobs/lms.yml -- plugins/drydock/k8s/drydock-jobs/cms.yml -- plugins/drydock/k8s/drydock-jobs/minio.yml -- plugins/drydock/k8s/drydock-jobs/forum.yml -- plugins/drydock/k8s/drydock-jobs/notes.yml +- plugins/drydock/k8s/jobs.yml {%- endif %} {% if DRYDOCK_FLOWER -%} - plugins/drydock/k8s/flower.yml diff --git a/drydock/plugin.py b/drydock/plugin.py index 9b86d45e..603f923f 100644 --- a/drydock/plugin.py +++ b/drydock/plugin.py @@ -1,11 +1,128 @@ from glob import glob import os +import click import pkg_resources -from tutor import hooks +import typing as t + +from .hooks import SYNC_WAVES_ORDER_ATTRS_TYPE, SYNC_WAVES_ORDER + +from tutor import hooks as tutor_hooks +from tutor import env as tutor_env +from tutor import serialize, types +from tutor import config as tutor_config from .__about__ import __version__ +INIT_JOBS_SYNC_WAVE = 1 + +# This function is taken from +# https://github.com/overhangio/tutor/blob/v15.3.7/tutor/commands/k8s.py#L180 +def _load_jobs(tutor_conf: types.Config) -> t.Iterable[t.Any]: + jobs = tutor_env.render_file(tutor_conf, "k8s", "jobs.yml").strip() + for manifest in serialize.load_all(jobs): + if manifest["kind"] == "Job": + yield manifest + + +# The definition of the init tasks is taken and adapted from +# https://github.com/overhangio/tutor/blob/v15.3.7/tutor/commands/jobs.py#L64 +# and https://github.com/overhangio/tutor/blob/v15.3.7/tutor/commands/k8s.py#L80 +def get_init_tasks(): + """Return the list of init tasks to run.""" + context = click.get_current_context().obj + tutor_conf = tutor_config.load(context.root) + + init_tasks = [] + # Standarize deprecated COMMANDS_INIT and COMMANDS_PRE_INIT Filter + for service, init_path in tutor_hooks.Filters.COMMANDS_PRE_INIT.iterate(): + init_tasks.append((service, tutor_env.read_template_file(*init_path))) + + init_tasks.extend(tutor_hooks.Filters.CLI_DO_INIT_TASKS.iterate()) + + for service, init_path in list(tutor_hooks.Filters.COMMANDS_INIT.iterate()): + init_tasks.append((service, tutor_env.read_template_file(*init_path))) + + for i, (service, command) in enumerate(init_tasks): + for template in _load_jobs(tutor_conf): + if template['metadata']['name'] != service + '-job': + continue + + render_command = tutor_env.render_str(tutor_conf, command) + + template['metadata']['name'] = f"drydock-{template['metadata']['name']}-{i}" + template['metadata']['labels'].update({ + 'app.kubernetes.io/component': 'drydock-job', + 'drydock.io/target-service': template['metadata']['name'], + 'drydock.io/runner-service': template['metadata']['name'] + }) + + template['metadata']['annotations'] = { + 'argocd.argoproj.io/sync-wave': INIT_JOBS_SYNC_WAVE + i * 2, + 'argocd.argoproj.io/hook': 'Sync', + 'argocd.argoproj.io/hook-delete-policy': 'HookSucceeded,BeforeHookCreation' + } + + shell_command = ["sh", "-e", "-c"] + if template["spec"]["template"]["spec"]["containers"][0].get("command") == []: + # In some cases, we need to bypass the container entrypoint. + # Unfortunately, AFAIK, there is no way to do so in K8s manifests. So we mark + # some jobs with "command: []". For these jobs, the entrypoint becomes "sh -e -c". + # We do not do this for every job, because some (most) entrypoints are actually useful. + template["spec"]["template"]["spec"]["containers"][0]["command"] = shell_command + container_args = [render_command] + else: + container_args = shell_command + [render_command] + + template["spec"]["template"]["spec"]["containers"][0]["args"] = container_args + template["spec"]["backoffLimit"] = 1 + template["spec"]["ttlSecondsAfterFinished"] = 3600 + + yield serialize.dumps(template) + + +CORE_SYNC_WAVES_ORDER: SYNC_WAVES_ORDER_ATTRS_TYPE = { + "drydock-upgrade-lms-job": 50, + "drydock-upgrade-cms-job": 51, + "lms-lifecycle-enabled": 100, + "cms-lifecycle-enabled": 100, + "lms-debug": 50, + "cms-debug": 50, + "ingress-debug": 200, + "horizontalpodautoscalers:all": 150 +} + + +@SYNC_WAVES_ORDER.add() +def _add_core_sync_waves_order(sync_waves_config: SYNC_WAVES_ORDER_ATTRS_TYPE) -> SYNC_WAVES_ORDER_ATTRS_TYPE: + sync_waves_config.update(CORE_SYNC_WAVES_ORDER) + return sync_waves_config + + +def get_sync_waves_order() -> SYNC_WAVES_ORDER_ATTRS_TYPE: + """ + Return the sync waves order for the plugin + """ + return SYNC_WAVES_ORDER.apply({}) + + +def iter_sync_waves_order() -> t.Iterable[SYNC_WAVES_ORDER_ATTRS_TYPE]: + """ + Yield: + (name, dict) + """ + yield from get_sync_waves_order().items() + + +def get_sync_waves_for_resource(resource_name: str) -> SYNC_WAVES_ORDER_ATTRS_TYPE: + """ + Args: + resource_name: the name of the resource + Returns: + dict + """ + return get_sync_waves_order().get(resource_name, 0) + ################# Configuration config = { @@ -15,6 +132,7 @@ "INIT_JOBS": False, "CMS_SSO_USER": "cms", "AUTO_TLS": True, + "MIGRATE_FROM": 0, "FLOWER": False, "INGRESS": False, "INGRESS_EXTRA_HOSTS": [], @@ -42,8 +160,8 @@ }, } -hooks.Filters.CONFIG_DEFAULTS.add_items([("OPENEDX_DEBUG_COOKIE", "ednx_enable_debug")]) -hooks.Filters.CONFIG_OVERRIDES.add_items([ +tutor_hooks.Filters.CONFIG_DEFAULTS.add_items([("OPENEDX_DEBUG_COOKIE", "ednx_enable_debug")]) +tutor_hooks.Filters.CONFIG_OVERRIDES.add_items([ # This values are not prefixed with DRYDOCK_ ("MONGODB_ROOT_USERNAME", ""), ("MONGODB_ROOT_PASSWORD", ""), @@ -54,16 +172,18 @@ ################# except maybe for educational purposes :) # Plugin templates -hooks.Filters.ENV_TEMPLATE_ROOTS.add_item( +tutor_hooks.Filters.ENV_TEMPLATE_ROOTS.add_item( pkg_resources.resource_filename("drydock", "templates") ) -hooks.Filters.ENV_TEMPLATE_TARGETS.add_items( + +tutor_hooks.Filters.ENV_TEMPLATE_TARGETS.add_items( [ ("drydock/build", "plugins"), ("drydock/apps", "plugins"), ("drydock/k8s", "plugins"), ], ) + # Load all patches from the "patches" folder for path in glob( os.path.join( @@ -72,19 +192,34 @@ ) ): with open(path, encoding="utf-8") as patch_file: - hooks.Filters.ENV_PATCHES.add_item((os.path.basename(path), patch_file.read())) + tutor_hooks.Filters.ENV_PATCHES.add_item((os.path.basename(path), patch_file.read())) # Load all configuration entries -hooks.Filters.CONFIG_DEFAULTS.add_items( +tutor_hooks.Filters.CONFIG_DEFAULTS.add_items( [ (f"DRYDOCK_{key}", value) for key, value in config["defaults"].items() ] ) -hooks.Filters.CONFIG_UNIQUE.add_items( +tutor_hooks.Filters.CONFIG_UNIQUE.add_items( [ (f"DRYDOCK_{key}", value) for key, value in config["unique"].items() ] ) -hooks.Filters.CONFIG_OVERRIDES.add_items(list(config["overrides"].items())) +tutor_hooks.Filters.CONFIG_OVERRIDES.add_items(list(config["overrides"].items())) + +tutor_hooks.Filters.ENV_TEMPLATE_VARIABLES.add_items( + [ + ('get_init_tasks', get_init_tasks), + ('iter_sync_waves_order', iter_sync_waves_order), + ('get_sync_waves_for_resource', get_sync_waves_for_resource), + ] +) + +# # init script +with open( + pkg_resources.resource_filename("drydock", "templates/drydock/task/mongodb/init"), + encoding="utf-8", +) as fi: + tutor_hooks.Filters.CLI_DO_INIT_TASKS.add_item(("mongodb", fi.read()), priority=tutor_hooks.priorities.HIGH) diff --git a/drydock/templates/drydock/k8s/debug/deployments.yml b/drydock/templates/drydock/k8s/debug/deployments.yml index 256e87a2..724431ae 100644 --- a/drydock/templates/drydock/k8s/debug/deployments.yml +++ b/drydock/templates/drydock/k8s/debug/deployments.yml @@ -5,6 +5,8 @@ metadata: name: cms-debug labels: app.kubernetes.io/name: cms-debug + annotations: + argocd.argoproj.io/sync-wave: "{{ get_sync_waves_for_resource('cms-debug') }}" spec: selector: matchLabels: @@ -57,6 +59,8 @@ metadata: name: lms-debug labels: app.kubernetes.io/name: lms-debug + annotations: + argocd.argoproj.io/sync-wave: "{{ get_sync_waves_for_resource('lms-debug') }}" spec: selector: matchLabels: diff --git a/drydock/templates/drydock/k8s/debug/ingress.yml b/drydock/templates/drydock/k8s/debug/ingress.yml index 4f244a51..ad748cf5 100644 --- a/drydock/templates/drydock/k8s/debug/ingress.yml +++ b/drydock/templates/drydock/k8s/debug/ingress.yml @@ -4,6 +4,7 @@ metadata: name: ingress-debug namespace: {{ K8S_NAMESPACE }} annotations: + argocd.argoproj.io/sync-wave: "{{ get_sync_waves_for_resource('ingress-debug') }}" kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-by-cookie: {{ OPENEDX_DEBUG_COOKIE }} diff --git a/drydock/templates/drydock/k8s/drydock-jobs/cms.yml b/drydock/templates/drydock/k8s/drydock-jobs/cms.yml deleted file mode 100644 index 366ee1f5..00000000 --- a/drydock/templates/drydock/k8s/drydock-jobs/cms.yml +++ /dev/null @@ -1,62 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: drydock-cms-job - labels: - drydock.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 - - -e - 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/drydock/k8s/drydock-jobs/forum.yml b/drydock/templates/drydock/k8s/drydock-jobs/forum.yml deleted file mode 100644 index 2a649b19..00000000 --- a/drydock/templates/drydock/k8s/drydock-jobs/forum.yml +++ /dev/null @@ -1,46 +0,0 @@ -{%- if FORUM_DOCKER_IMAGE is defined %} -apiVersion: batch/v1 -kind: Job -metadata: - name: drydock-forum-job - labels: - drydock.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/drydock/k8s/drydock-jobs/lms.yml b/drydock/templates/drydock/k8s/drydock-jobs/lms.yml deleted file mode 100644 index 1b004f9c..00000000 --- a/drydock/templates/drydock/k8s/drydock-jobs/lms.yml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: drydock-lms-job - labels: - drydock.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 - - -e - 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/drydock/k8s/drydock-jobs/minio.yml b/drydock/templates/drydock/k8s/drydock-jobs/minio.yml deleted file mode 100644 index a8b458ef..00000000 --- a/drydock/templates/drydock/k8s/drydock-jobs/minio.yml +++ /dev/null @@ -1,38 +0,0 @@ -{%- if MINIO_DOCKER_IMAGE is defined %} -apiVersion: batch/v1 -kind: Job -metadata: - name: drydock-minio-job - labels: - drydock.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 - - -e - 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 %} diff --git a/drydock/templates/drydock/k8s/drydock-jobs/mongodb.yml b/drydock/templates/drydock/k8s/drydock-jobs/mongodb.yml deleted file mode 100644 index 88ad9337..00000000 --- a/drydock/templates/drydock/k8s/drydock-jobs/mongodb.yml +++ /dev/null @@ -1,52 +0,0 @@ - -apiVersion: batch/v1 -kind: Job -metadata: - name: drydock-mongodb-job - labels: - drydock.io/component: job - drydock.io/target-service: mongodb - drydock.io/runner-service: mongodb - 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: mongodb - image: {{ DOCKER_IMAGE_MONGODB }} - command: - - /bin/sh - - -c - - -e - args: - - | - echo "Initialising MongoDB..." - mongo --host {{MONGODB_HOST }} -u "{{ MONGODB_ROOT_USERNAME }}" -p "{{ MONGODB_ROOT_PASSWORD }}" admin <&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/drydock/k8s/drydock-jobs/notes.yml b/drydock/templates/drydock/k8s/drydock-jobs/notes.yml deleted file mode 100644 index fb07f3b5..00000000 --- a/drydock/templates/drydock/k8s/drydock-jobs/notes.yml +++ /dev/null @@ -1,132 +0,0 @@ -{%- if NOTES_DOCKER_IMAGE is defined %} -apiVersion: batch/v1 -kind: Job -metadata: - name: drydock-notes-job-mysql - labels: - drydock.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 - - -e - 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: drydock-notes-job - labels: - drydock.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 - - -e - 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: drydock-notes-job-lms - labels: - drydock.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 - - -e - 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/drydock/k8s/forum-overrides.yml b/drydock/templates/drydock/k8s/forum-overrides.yml deleted file mode 100644 index e69de29b..00000000 diff --git a/drydock/templates/drydock/k8s/jobs.yml b/drydock/templates/drydock/k8s/jobs.yml new file mode 100644 index 00000000..a733df71 --- /dev/null +++ b/drydock/templates/drydock/k8s/jobs.yml @@ -0,0 +1,113 @@ +{% for job in get_init_tasks() %} +{{ job }} +--- +{% endfor %} +{% if DRYDOCK_MIGRATE_FROM %} +apiVersion: batch/v1 +kind: Job +metadata: + name: drydock-upgrade-lms-job + labels: + drydock.io/component: job + drydock.io/target-service: lms + drydock.io/runner-service: lms + annotations: + argocd.argoproj.io/sync-wave: "{{ get_sync_waves_for_resource('drydock-upgrade-lms-job') }}" + 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 + - -e + args: + - | + {%- if DRYDOCK_MIGRATE_FROM == 13 %} + ./manage.py lms backpopulate_user_tours + {%- endif %} + {%- if DRYDOCK_MIGRATE_FROM in (13,14) %} + ./manage.py lms compute_grades -v1 --all_courses + {%- endif %} + 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: drydock-upgrade-cms-job + labels: + drydock.io/component: job + drydock.io/target-service: cms + drydock.io/runner-service: cms + annotations: + argocd.argoproj.io/sync-wave: "{{ get_sync_waves_for_resource('drydock-upgrade-cms-job') }}" + 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 + - -e + args: + - | + {%- if DRYDOCK_MIGRATE_FROM == 13 %} + ./manage.py cms backfill_course_tabs + ./manage.py cms simulate_publish + {%- endif %} + 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 +{% endif %} diff --git a/drydock/templates/drydock/k8s/lifecycle/cms.yml b/drydock/templates/drydock/k8s/lifecycle/cms.yml index ebd6ac75..f55d98dc 100644 --- a/drydock/templates/drydock/k8s/lifecycle/cms.yml +++ b/drydock/templates/drydock/k8s/lifecycle/cms.yml @@ -2,6 +2,8 @@ apiVersion: apps/v1 kind: Deployment metadata: name: cms + annotations: + argocd.argoproj.io/sync-wave: "{{ get_sync_waves_for_resource('cms-lifecycle-enabled') }}" spec: template: spec: diff --git a/drydock/templates/drydock/k8s/lifecycle/lms.yml b/drydock/templates/drydock/k8s/lifecycle/lms.yml index fc12f805..ff67e306 100644 --- a/drydock/templates/drydock/k8s/lifecycle/lms.yml +++ b/drydock/templates/drydock/k8s/lifecycle/lms.yml @@ -2,6 +2,8 @@ apiVersion: apps/v1 kind: Deployment metadata: name: lms + annotations: + argocd.argoproj.io/sync-wave: "{{ get_sync_waves_for_resource('lms-lifecycle-enabled') }}" spec: template: spec: diff --git a/drydock/templates/drydock/k8s/patches/hpa-sync-wave.yml b/drydock/templates/drydock/k8s/patches/hpa-sync-wave.yml new file mode 100644 index 00000000..5c4ef544 --- /dev/null +++ b/drydock/templates/drydock/k8s/patches/hpa-sync-wave.yml @@ -0,0 +1,8 @@ +apiVersion: not-used +kind: not-used +metadata: + name: not-used + annotations: + argocd.argoproj.io/sync-wave: "{{ get_sync_waves_for_resource('horizontalpodautoscalers:all') }}" + argocd.argoproj.io/hook: Sync + argocd.argoproj.io/hook-delete-policy: HookSucceeded diff --git a/drydock/templates/drydock/k8s/patches/sync-wave-4.yml b/drydock/templates/drydock/k8s/patches/sync-wave-4.yml deleted file mode 100644 index dffb3fdd..00000000 --- a/drydock/templates/drydock/k8s/patches/sync-wave-4.yml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: not-used -kind: not-used -metadata: - name: not-used - annotations: - argocd.argoproj.io/sync-wave: "4" diff --git a/drydock/templates/drydock/k8s/patches/sync-wave-5.yml b/drydock/templates/drydock/k8s/patches/sync-wave-5.yml deleted file mode 100644 index c2471ee7..00000000 --- a/drydock/templates/drydock/k8s/patches/sync-wave-5.yml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: not-used -kind: not-used -metadata: - name: not-used - annotations: - argocd.argoproj.io/sync-wave: "5" diff --git a/drydock/templates/drydock/task/mongodb/init b/drydock/templates/drydock/task/mongodb/init new file mode 100644 index 00000000..fa603594 --- /dev/null +++ b/drydock/templates/drydock/task/mongodb/init @@ -0,0 +1,25 @@ +echo "Initialising MongoDB..." +mongo --host {{MONGODB_HOST }} {% if MONGODB_ROOT_USERNAME and MONGODB_ROOT_PASSWORD %} -u {{ MONGODB_ROOT_USERNAME }} -p {{ MONGODB_ROOT_PASSWORD }} {% endif %} admin <