diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 000000000..794468183 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,33 @@ +FROM ubuntu:24.04 +LABEL maintainer="Klavs Klavsen " + +WORKDIR /tmp +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update && \ + apt-get install -y \ + python3-pip \ + python3-venv \ + build-essential \ + postgresql-server-dev-all + +RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get install git gettext-base curl -y +RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash - +RUN apt-get update; DEBIAN_FRONTEND=noninteractive apt-get install -y nodejs + +WORKDIR /var/www +RUN python3 -m venv relate-venv +RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python3-poetry + + +RUN git clone https://github.com/inducer/relate.git +ENV VENV_PATH=/var/www/relate-venv +WORKDIR /var/www/relate +RUN git pull +RUN poetry install +RUN npm install ; npm run build +RUN poetry run pip install psycopg2 +COPY local_settings_template.py /var/www/relate/local_settings_template.py +COPY run-relate.sh /run-relate.sh +RUN chmod 755 /run-relate.sh + +CMD ["/run-relate.sh"] diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 000000000..0c841c7ca --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,25 @@ +version: '3' + +services: + app: + image: relate:latest + restart: unless-stopped + command: /run-relate.sh + environment: + DBHOST: db + DBUSER: postgres + DBPASS: LocalPassword + ports: + - 8000:8000 + links: + - db + + db: + image: postgres + restart: unless-stopped + ports: + - 5432:5432 + environment: + POSTGRES_PASSWORD: LocalPassword + POSTGRES_DB: relate + diff --git a/docker/local_settings_template.py b/docker/local_settings_template.py new file mode 100644 index 000000000..4527d6323 --- /dev/null +++ b/docker/local_settings_template.py @@ -0,0 +1,636 @@ +# See https://docs.djangoproject.com/en/dev/howto/deployment/checklist/ + +import os.path as path +import os + +_BASEDIR = path.dirname(path.abspath(__file__)) + +# {{{ database and site + +# TODO: Need to find out WHY this needs to be set in production - and ensure its set from a secret - so it stays the same +SECRET_KEY = "" + +ALLOWED_HOSTS = [ $EXTRAHOSTS ] + +CSRF_TRUSTED_ORIGINS = [ $ORIGINS ] + +# Configure the following as url as above. +RELATE_BASE_URL = "https://relate.obmondo.com" + +from django.utils.translation import gettext_noop # noqa + +# Uncomment this to configure the site name of your relate instance. +# If not configured, "RELATE" will be used as default value. +# Use gettext_noop() if you want it to be discovered as an i18n literal +# for translation. +#RELATE_CUTOMIZED_SITE_NAME = gettext_noop("My RELATE") + +# Uncomment this to use a real database. If left commented out, a local SQLite3 +# database will be used, which is not recommended for production use. +# +DATABASES = { + "default": { + "ENGINE": "django.db.backends.postgresql", + "NAME": "relate", + "USER": "$DBUSER", + "PASSWORD": '$DBPASS', + "HOST": '$DBHOST', + "PORT": '5432', + } + } + +# Recommended, because dulwich is kind of slow in retrieving stuff. +# +# Also, progress bars for long-running operations will only work +# properly if you enable this. (or a similar out-of-process cache +# backend) +# +# You must 'pip install pylibmc' to use this (which in turn may require +# installing 'libmemcached-dev'). +# +# Btw, do not be tempted to use 'MemcachedCache'--it's unmaintained and +# broken in Python 33, as of 2016-08-01. +# +# CACHES = { +# "default": { +# "BACKEND": "django.core.cache.backends.memcached.PyLibMCCache", +# "LOCATION": '127.0.0.1:11211', +# } +# } + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = False + +TIME_ZONE = "Europe/Copenhagen" + +# RELATE needs a message broker for long-running tasks. +# +# See here for options: +# http://docs.celeryproject.org/en/latest/userguide/configuration.html#broker-url +# +# The dev server will run fine without this, but any tasks that require +# queueing will just appear to hang. On Debian/Ubuntu, the following line +# should be enough to satisfy this requirement. +# +# apt-get install rabbitmq-server +CELERY_BROKER_URL = "amqp://rabbitmq" +# }}} + +# {{{ git storage + +# Your course git repositories will be stored under this directory. +# Make sure it's writable by your web user. +# +# The "course identifiers" you enter will be directory names below this root. + +#GIT_ROOT = "/some/where" +GIT_ROOT = path.join(_BASEDIR, "git-roots") + +# }}} + +# {{{ bulk storage + +from django.core.files.storage import FileSystemStorage +# This must be a subclass of django.core.storage.Storage. +# This should *not* be MEDIA_ROOT, and the corresponding directory/storage location +# should *not* be accessible under a URL. +RELATE_BULK_STORAGE = FileSystemStorage(path.join(_BASEDIR, "bulk-storage")) + +# }}} + +# {{{ email + +EMAIL_HOST = "$SMTPHOST" +EMAIL_HOST_USER = "" +EMAIL_HOST_PASSWORD = "" +EMAIL_PORT = $SMTPPORT +EMAIL_USE_TLS = False + +ROBOT_EMAIL_FROM = "Klavs Klavsen " +RELATE_ADMIN_EMAIL_LOCALE = "en_US" + +SERVER_EMAIL = ROBOT_EMAIL_FROM + +ADMINS = ( + ("Klavs Klavsen", "klavs@enableit.dk"), + ) + +# If your email service do not allow nonauthorized sender, uncomment the following +# statement and change the configurations above accordingly, noticing that all +# emails will be sent using the EMAIL_ settings above. +#RELATE_EMAIL_SMTP_ALLOW_NONAUTHORIZED_SENDER = False + +# Advanced email settings if you want to configure multiple SMTPs for different +# purpose/type of emails. It is also very useful when +# "RELATE_EMAIL_SMTP_ALLOW_NONAUTHORIZED_SENDER" is False. +# If you want to enable this functionality, set the next line to True, and edit +# the next block with your cofigurations. +RELATE_ENABLE_MULTIPLE_SMTP = False + +if RELATE_ENABLE_MULTIPLE_SMTP: + EMAIL_CONNECTIONS = { + + # For automatic email sent by site. + "robot": { + # You can use your preferred email backend. + "backend": "djcelery_email.backends.CeleryEmailBackend", + "host": "smtp.gmail.com", + "username": "blah@blah.com", + "password": "password", + "port": 587, + "use_tls": True, + }, + + # For emails that expect no reply for recipients, e.g., registration, + # reset password, etc. + "no_reply": { + "host": "smtp.gmail.com", + "username": "blah@blah.com", + "password": "password", + "port": 587, + "use_tls": True, + }, + + # For sending notifications like submission of flow sessions. + "notification": { + "host": "smtp.gmail.com", + "username": "blah@blah.com", + "password": "password", + "port": 587, + "use_tls": True, + }, + + # For sending feedback email to students in grading interface. + "grader_feedback": { + "host": "smtp.gmail.com", + "username": "blah@blah.com", + "password": "password", + "port": 587, + "use_tls": True, + }, + + # For student to send email to course staff in flow pages + "student_interact": { + "host": "smtp.gmail.com", + "username": "blah@blah.com", + "password": "password", + "port": 587, + "use_tls": True, + }, + + # For enrollment request email sent to course instructors + "enroll": { + "host": "smtp.gmail.com", + "username": "blah@blah.com", + "password": "password", + "port": 587, + "use_tls": True, + }, + } + + # This will be used as default connection when other keys are not set. + EMAIL_CONNECTION_DEFAULT = "robot" + + NO_REPLY_EMAIL_FROM = "Noreply " + NOTIFICATION_EMAIL_FROM = "Notification " + GRADER_FEEDBACK_EMAIL_FROM = "Feedback " + STUDENT_INTERACT_EMAIL_FROM = "interaction " + ENROLLMENT_EMAIL_FROM = "Enrollment " + + +# }}} + + +# Cool down time (seconds) required before another new session of a flow +# is allowed to be started. +RELATE_SESSION_RESTART_COOLDOWN_SECONDS = 10 + + +# {{{ sign-in methods + +RELATE_SIGN_IN_BY_EMAIL_ENABLED = False +RELATE_SIGN_IN_BY_USERNAME_ENABLED = True +RELATE_REGISTRATION_ENABLED = False +RELATE_SIGN_IN_BY_EXAM_TICKETS_ENABLED = False + +# If you enable this, you must also have saml_config.py in this directory. +# See saml_config.py.example for help. +RELATE_SIGN_IN_BY_SAML2_ENABLED = False + +RELATE_SOCIAL_AUTH_BACKENDS = ( + # See https://python-social-auth.readthedocs.io/en/latest/ + # for full list. + 'social_core.backends.keycloak.KeycloakOAuth2', + 'django.contrib.auth.backends.ModelBackend', + # CAUTION: Relate uses emails returned by the backend to match + # users. Only use backends that return verified emails. + ) + + +SOCIAL_AUTH_KEYCLOAK_ID_KEY = 'email' +SOCIAL_AUTH_KEYCLOAK_PUBLIC_KEY= os.environ.get('KEYCLOAK_PUBLICKEY') +SOCIAL_AUTH_KEYCLOAK_KEY = 'relate' +SOCIAL_AUTH_KEYCLOAK_SECRET = os.environ.get('KEYCLOAK_SECRET') +SOCIAL_AUTH_KEYCLOAK_SERVER_URL = 'https:///realms/test-relate/' # required to fill +SOCIAL_AUTH_KEYCLOAK_AUTHORIZATION_URL = \ + 'https:///auth/realms/test-relate/protocol/openid-connect/auth' # required to fill +SOCIAL_AUTH_KEYCLOAK_ACCESS_TOKEN_URL = \ + 'https:///auth/realms/test-relate/protocol/openid-connect/token' # required to fill +SOCIAL_AUTH_REDIRECT_IS_HTTPS = True +SOCIAL_AUTH_SESSION_EXPIRATION = True + + + +# Set the "SOCIAL_AUTH_LOGIN_REDIRECT_URL" to a page you want to redirect to after login +# SOCIAL_AUTH_LOGIN_REDIRECT_URL = 'https://relate.kbm.obmondo.com/social-auth/complete/keycloak/' +# When registering your OAuth2 app (and consent screen) with Google, +# specify the following authorized redirect URI: +# https://sitename.edu/social-auth/complete/google-oauth2/ + +# Blacklist these domains for social auth. This may be useful if there +# is a canonical way (e.g. SAML2) for members of that domain to +# sign in. +# RELATE_SOCIAL_AUTH_BLACKLIST_EMAIL_DOMAINS = { +# "illinois.edu": "Must use SAML2 to sign in." +# } + +# }}} + +# {{{ editable institutional id before verification? + +# If set to False, user won't be able to edit institutional ID +# after submission. Set to False only when you trust your students +# or you don't want to verfiy insitutional ID they submit. +RELATE_EDITABLE_INST_ID_BEFORE_VERIFICATION = True + +# If set to False, these fields will be hidden in the user profile form. +RELATE_SHOW_INST_ID_FORM = True +RELATE_SHOW_EDITOR_FORM = True + +# }}} + +# Whether disable "markdown.extensions.codehilite" when rendering page markdown. +# Default to True, as enable it sometimes crashes for some pages with code fences. +# For this reason, there will be a warning when the attribute is set to False when +# starting the server. +#RELATE_DISABLE_CODEHILITE_MARKDOWN_EXTENSION = True + +# {{{ user full_name format + +# RELATE's default full_name format is "'%s %s' % (first_name, last_name)", +# you can override it by supply a customized method/fuction, with +# "firstname" and "lastname" as its paramaters, and return a string. + +# For example, you can define it like this: + +# +# def my_fullname_format(firstname, lastname): +# return "%s%s" % (last_name, first_name) +# + +# and then uncomment the following line and enable it with: + +#RELATE_USER_FULL_NAME_FORMAT_METHOD = my_fullname_format + +# You can also import it from your custom module, or use a dotted path of the +# method, i.e.: +#RELATE_USER_FULL_NAME_FORMAT_METHOD = "path.to.my_fullname_format" + +# }}} + +# {{{ system email appellation priority + +# RELATE's default email appellation of the receiver is a ordered list: +# ["first_name", "email", "username"], when first_name is not None +# (e.g, first_name = "Foo"), the email will be opened +# by "Dear Foo,". If first_name is None, then email will be used +# as appellation, so on and so forth. + +# you can override the appellation priority by supply a customized list +# named relate_email_appellation_priority_list. The available +# elements include first_name, last_name, get_full_name, email and +# username. + +# RELATE_EMAIL_APPELLATION_PRIORITY_LIST = [ +# "full_name", "first_name", "email", "username"] + +# }}} + +# {{{ custom method for masking user profile +# When a participation, for example, teaching assistant, has limited access to +# students' profile (i.e., has_permission(pperm.view_participant_masked_profile)), +# a built-in mask method (which is based on pk of user instances) is used be +# default. The mask method can be overriden by the following a custom method, with +# user as the args. + +#RELATE_USER_PROFILE_MASK_METHOD = "path.tomy_method +# For example, you can define it like this: + +# +# def my_mask_method(user): +# return "User_%s" % str(user.pk + 100) +# + +# and then uncomment the following line and enable it with: + +#RELATE_USER_PROFILE_MASK_METHOD = my_mask_method + +# You can also import it from your custom module, or use a dotted path of the +# method, i.e.: +#RELATE_USER_PROFILE_MASK_METHOD = "path.to.my_mask_method" + +# }}} + +# {{{ extra checks + +# This allow user to add customized startup checkes for user-defined modules +# using Django's system checks (https://docs.djangoproject.com/en/dev/ref/checks/) +# For example, define a `my_check_func in `my_module` with +# +# def my_check_func(app_configs, **kwargs): +# return [list of error] +# +# The configuration should be +# RELATE_STARTUP_CHECKS_EXTRA = ["my_module.my_check_func"] +# i.e., Each item should be the path to an importable check function. +#RELATE_STARTUP_CHECKS_EXTRA = [] + +# }}} + +# {{{ overriding built-in templates +# Uncomment the following to enable templates overriding. It should be configured +# as a list/tuple of path(s). +# For example, if you the templates are in a folder named "my_templates" in the +# root dir of the project, with base.html (project template), course_base.html, +# and sign-in-email.txt (app templates) etc., are the templates you want to +# override, the structure of the files should look like: +# ... +# relate/ +# local_settings.py +# my_templates/ +# base.html +# ... +# course/ +# course_base.html +# sign-in-email.txt +# ... +# + +# import os.path +# RELATE_OVERRIDE_TEMPLATES_DIRS = [ +# os.path.join(os.path.dirname(__file__), "my_templates"), +# os.path.join(os.path.dirname(__file__), "my_other_templates") +# ] + +# }}} + +# {{{ docker + +# A string containing the image ID of the docker image to be used to run +# student Python code. Docker should download the image on first run. +RELATE_DOCKER_RUNPY_IMAGE = "inducer/relate-runcode-python-amd64" +# RELATE_DOCKER_RUNPY_IMAGE = "inducer/relate-runpy-amd64-tensorflow" +# (bigger, but includes TensorFlow) + +# A URL pointing to the Docker command interface which RELATE should use +# to spawn containers for student code. +RELATE_DOCKER_URL = "unix://var/run/docker.sock" + +RELATE_DOCKER_TLS_CONFIG = None + +# Example setup for targeting remote Docker instances +# with TLS authentication: + +# RELATE_DOCKER_URL = "https://relate.cs.illinois.edu:2375" +# +# import os.path +# pki_base_dir = os.path.dirname(__file__) +# +# import docker.tls +# RELATE_DOCKER_TLS_CONFIG = docker.tls.TLSConfig( +# client_cert=( +# os.path.join(pki_base_dir, "client-cert.pem"), +# os.path.join(pki_base_dir, "client-key.pem"), +# ), +# ca_cert=os.path.join(pki_base_dir, "ca.pem"), +# verify=True) + +# }}} + +# {{{ maintenance and announcements + +RELATE_MAINTENANCE_MODE = False + +RELATE_MAINTENANCE_MODE_EXCEPTIONS = [] +# RELATE_MAINTENANCE_MODE_EXCEPTIONS = ["192.168.1.0/24"] + +# May be set to a string to set a sitewide announcement visible on every page. +RELATE_SITE_ANNOUNCEMENT = None + +# }}} + +# Uncomment this to enable i18n, change "en-us" to locale name your language. +# Make sure you have generated, translate and compile the message file of your +# language. If commented, RELATE will use default language "en-us". + +#LANGUAGE_CODE = "en-us" + +# You can (and it's recommended to) override Django's built-in LANGUAGES settings +# if you want to filter languages allowed for course-specific languages. +# The format of languages should be a list/tuple of 2-tuples: +# (language_code, language_description). If there are entries with the same +# language_code, language_description will be using the one which comes latest. +#.If LANGUAGES is not configured, django.conf.global_settings.LANGUAGES will be +# used. +# Note: make sure LANGUAGE_CODE you used is also in LANGUAGES, if it is not +# the default "en-us". Otherwise translation of that language will not work. + +# LANGUAGES = [ +# ("en", "English"), +# ("zh-hans", "Simplified Chinese"), +# ("de", "German"), +# ] + +# {{{ exams and testing + +# This may also be a callable that receives a local-timezone datetime and returns +# an equivalent dictionary. +# +# def RELATE_FACILITIES(now_datetime): +# from relate.utils import localize_datetime +# from datetime import datetime +# +# if (now_datetime >= localize_datetime(datetime(2016, 5, 5, 0, 0)) +# and now_datetime < localize_datetime(datetime(2016, 5, 6, 0, 0))): +# ip_ranges = [ +# "127.0.0.1/32", +# "192.168.77.0/24", +# ] +# else: +# ip_ranges = [] +# +# return { +# "test_center": { +# "ip_ranges": ip_ranges, +# "exams_only": True, +# }, +# } + + +RELATE_FACILITIES = { + "test_center": { + "ip_ranges": [ + "192.168.192.0/24", + ], + "exams_only": False, + }, +} + +# For how many minutes is an exam ticket still usable for login after its first +# use? +RELATE_TICKET_MINUTES_VALID_AFTER_USE = 12*60 + +# }}} + +# {{{ saml2 (optional) + +if RELATE_SIGN_IN_BY_SAML2_ENABLED: + from os import path + import saml2.saml + _BASE_URL = "https://relate.cs.illinois.edu" + + # see saml2-keygen.sh in this directory + _SAML_KEY_FILE = path.join(_BASEDIR, "saml-config", "sp-key.pem") + _SAML_CERT_FILE = path.join(_BASEDIR, "saml-config", "sp-cert.pem") + + SAML_ATTRIBUTE_MAPPING = { + "eduPersonPrincipalName": ("username",), + "iTrustUIN": ("institutional_id",), + "mail": ("email",), + "givenName": ("first_name", ), + "sn": ("last_name", ), + } + SAML_DJANGO_USER_MAIN_ATTRIBUTE = "username" + SAML_DJANGO_USER_MAIN_ATTRIBUTE_LOOKUP = "__iexact" + + saml_idp = { + # Find the entity ID of your IdP and make this the key here: + "urn:mace:incommon:uiuc.edu": { + "single_sign_on_service": { + # Add the POST and REDIRECT bindings for the sign on service here: + saml2.BINDING_HTTP_POST: + "https://shibboleth.illinois.edu/idp/profile/SAML2/POST/SSO", + saml2.BINDING_HTTP_REDIRECT: + "https://shibboleth.illinois.edu/idp/profile/SAML2/Redirect/SSO", + }, + "single_logout_service": { + # And the REDIRECT binding for the logout service here: + saml2.BINDING_HTTP_REDIRECT: + "https://shibboleth.illinois.edu/idp/logout.jsp", # noqa + }, + }, + } + + SAML_CONFIG = { + # full path to the xmlsec1 binary programm + "xmlsec_binary": "/usr/bin/xmlsec1", + + # your entity id, usually your subdomain plus the url to the metadata view + # (usually no need to change) + "entityid": _BASE_URL + "/saml2/metadata/", + + # directory with attribute mapping + # (already populated with samples from djangosaml2, usually no need to + # change) + "attribute_map_dir": path.join(_BASEDIR, "saml-config", "attribute-maps"), + + "allow_unknown_attributes": True, + + # this block states what services we provide + "service": { + "sp": { + "name": "RELATE SAML2 SP", + + # Django sets SameSite attribute on session cookies, + # which causes problems. Work around that, for now. + # https://github.com/peppelinux/djangosaml2/issues/143#issuecomment-633694504 + "allow_unsolicited": True, + + "name_id_format": saml2.saml.NAMEID_FORMAT_TRANSIENT, + "endpoints": { + # url and binding to the assertion consumer service view + # do not change the binding or service name + "assertion_consumer_service": [ + (_BASE_URL + "/saml2/acs/", + saml2.BINDING_HTTP_POST), + ], + # url and binding to the single logout service view + # do not change the binding or service name + "single_logout_service": [ + (_BASE_URL + "/saml2/ls/", + saml2.BINDING_HTTP_REDIRECT), + (_BASE_URL + "/saml2/ls/post", + saml2.BINDING_HTTP_POST), + ], + }, + + # attributes that this project needs to identify a user + "required_attributes": ["uid"], + + # attributes that may be useful to have but not required + "optional_attributes": ["eduPersonAffiliation"], + + "idp": saml_idp, + }, + }, + + # You will get this XML file from your institution. It has finite validity + # and will need to be re-downloaded periodically. + # + # "itrust" is an example name that's valid for the University of Illinois. + # This particular file is public and lives at + # https://discovery.itrust.illinois.edu/itrust-metadata/itrust-metadata.xml + + "metadata": { + "local": [path.join(_BASEDIR, "saml-config", "itrust-metadata.xml")], + }, + + # set to 1 to output debugging information + "debug": 1, + + # certificate and key + "key_file": _SAML_KEY_FILE, + "cert_file": _SAML_CERT_FILE, + + "encryption_keypairs": [ + { + "key_file": _SAML_KEY_FILE, + "cert_file": _SAML_CERT_FILE, + } + ], + + # own metadata settings + "contact_person": [ + {"given_name": "Andreas", + "sur_name": "Kloeckner", + "company": "CS - University of Illinois", + "email_address": "andreask@illinois.edu", + "contact_type": "technical"}, + {"given_name": "Andreas", + "sur_name": "Kloeckner", + "company": "CS - University of Illinois", + "email_address": "andreask@illinois.edu", + "contact_type": "administrative"}, + ], + # you can set multilanguage information here + "organization": { + "name": [("RELATE", "en")], + "display_name": [("RELATE", "en")], + "url": [(_BASE_URL, "en")], + }, + "valid_for": 24, # how long is our metadata valid + } + +# }}} + +# vim: filetype=python:foldmethod=marker diff --git a/docker/run-relate.sh b/docker/run-relate.sh new file mode 100755 index 000000000..875048762 --- /dev/null +++ b/docker/run-relate.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +# verify we HAVE necessary env vars +if [ -z "$DBUSER" ] || [ -z "$DBPASS" ] || [ -z "$DBHOST" ]; then + echo "Missing necessary environment variables!" + env + exit 1 +fi + +set -x + +#set extra host headers +EXTRAHOSTS= +if [ ! -z "$HOSTS" ]; then + IFS=";" + for h in $HOSTS + do + if [ -z "${EXTRAHOSTS}" ]; then + EXTRAHOSTS="\"$h\"" + else + EXTRAHOSTS="${EXTRAHOSTS},\"${h}\"" + fi + done + unset IFS + export EXTRAHOSTS +fi +ORIGINS= +if [ ! -z "$HOSTS" ]; then + IFS=";" + for h in $HOSTS + do + if [ -z "${ORIGINS}" ]; then + ORIGINS="\"https://$h\"" + else + ORIGINS="${ORIGINS},\"https://${h}\"" + fi + done + unset IFS + export ORIGINS +fi + +set -euo pipefail + +envsubst /var/www/relate/local_settings.py + +cd /var/www/relate + +# create initial db setup if necessary - and superuser +find /var/www/relate/static -mindepth 1 -delete || true +poetry run python manage.py collectstatic +poetry run python manage.py migrate --verbosity 3 +poetry run python manage.py createsuperuser --username=relateadmin + +# run directly instead of via uwsgi +poetry run python manage.py runserver 0.0.0.0:8000 + +# find name of virtual env - for use in uwsgi config +VENV=$(find /root/.cache/pypoetry/virtualenvs/ -name 'relate-courseware*') +echo "$VENV" + + diff --git a/helm/relate/Chart.yaml b/helm/relate/Chart.yaml new file mode 100644 index 000000000..815499ebf --- /dev/null +++ b/helm/relate/Chart.yaml @@ -0,0 +1,4 @@ +apiVersion: v2 +name: relate +version: 0.1 +AppVersion: 20241223 diff --git a/helm/relate/readme.md b/helm/relate/readme.md new file mode 100644 index 000000000..f5d3ba60c --- /dev/null +++ b/helm/relate/readme.md @@ -0,0 +1,23 @@ +# Before installing + + This Helm chart expects you to have the following Helm charts installed: + + - https://cloudnative-pg.github.io/charts + - https://github.com/bitnami/charts/tree/master/bitnami/rabbitmq-cluster-operator + + AND you need to have a working mail server to point it to. + - IF you do not have one - you can use this chart to setup a mailrelay service in your Kubernetes cluster: https://github.com/bokysan/docker-postfix/blob/master/helm/mail + + and currently no image is pushed to any dockerhub - so you need to do that - and point to it. + +For additional configuration options for social authentication using Python, refer to these docs. These changes should be made in the ``local_settings_template.py`` file present in ``docker`` folder. currently it is configured to use keycloak authentication. +- https://python-social-auth.readthedocs.io/en/latest/backends/keycloak.html +- https://python-social-auth.readthedocs.io/en/latest/configuration/settings.html#urls-options + +# After installing + +After spinning up relate - you need to open a shell in the relate pod and run this to create your initial admin user: +``` +poetry run python manage.py createsuperuser --username=youradminuser +``` + diff --git a/helm/relate/templates/_helpers.tpl b/helm/relate/templates/_helpers.tpl new file mode 100644 index 000000000..76faa3a68 --- /dev/null +++ b/helm/relate/templates/_helpers.tpl @@ -0,0 +1,191 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "relate.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "relate.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Expand to the namespace relate installs into. +*/}} +{{- define "relate.namespace" -}} +{{- default .Release.Namespace .Values.namespace -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "relate.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Kubernetes standard labels +*/}} +{{- define "relate.labels" -}} +app.kubernetes.io/name: {{ include "relate.name" . }} +helm.sh/chart: {{ include "relate.chart" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/version: "{{ .Chart.AppVersion }}" +{{- end -}} + +{{/* +Labels to use on deploy.spec.selector.matchLabels and svc.spec.selector +*/}} +{{- define "relate.matchLabels" -}} +app.kubernetes.io/name: {{ include "relate.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{/* +Return true if cert-manager required annotations for TLS signed certificates are set in the Ingress annotations +Ref: https://cert-manager.io/docs/usage/ingress/#supported-annotations +*/}} +{{- define "relate.ingress.certManagerRequest" -}} +{{ if or (hasKey . "cert-manager.io/cluster-issuer") (hasKey . "cert-manager.io/issuer") }} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Renders a value that contains template. +Usage: +{{ include "relate.render" ( dict "value" .Values.path.to.the.Value "context" $) }} +*/}} +{{- define "relate.render" -}} + {{- if typeIs "string" .value }} + {{- tpl .value .context }} + {{- else }} + {{- tpl (.value | toYaml) .context }} + {{- end }} +{{- end -}} + +{{/* +Return the target Kubernetes version +*/}} +{{- define "relate.kubeVersion" -}} +{{- if .Values.global }} + {{- if .Values.global.kubeVersion }} + {{- .Values.global.kubeVersion -}} + {{- else }} + {{- default .Capabilities.KubeVersion.Version .Values.kubeVersion -}} + {{- end -}} +{{- else }} +{{- default .Capabilities.KubeVersion.Version .Values.kubeVersion -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for deployment. +*/}} +{{- define "relate.deployment.apiVersion" -}} +{{- if semverCompare "<1.14-0" (include "relate.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else -}} +{{- print "apps/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for ingress. +*/}} +{{- define "relate.ingress.apiVersion" -}} +{{- if .Values.ingress -}} +{{- if .Values.ingress.apiVersion -}} +{{- .Values.ingress.apiVersion -}} +{{- else if semverCompare "<1.14-0" (include "relate.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else if semverCompare "<1.19-0" (include "relate.kubeVersion" .) -}} +{{- print "networking.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "networking.k8s.io/v1" -}} +{{- end }} +{{- else if semverCompare "<1.14-0" (include "relate.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else if semverCompare "<1.19-0" (include "relate.kubeVersion" .) -}} +{{- print "networking.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "networking.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for networkpolicy. +*/}} +{{- define "relate.networkPolicy.apiVersion" -}} +{{- if semverCompare "<1.7-0" (include "relate.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else -}} +{{- print "networking.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +Usage: +{{ include "relate.backend" (dict "serviceName" "backendName" "servicePort" "backendPort" "context" $) }} + +Params: + - serviceName - String. Name of an existing service backend + - servicePort - String/Int. Port name (or number) of the service. It will be translated to different yaml depending if it is a string or an integer. + - context - Dict - Required. The context for the template evaluation. +*/}} +{{- define "relate.backend" -}} +{{- $apiVersion := (include "relate.ingress.apiVersion" .context) -}} +{{- if or (eq $apiVersion "extensions/v1beta1") (eq $apiVersion "networking.k8s.io/v1beta1") -}} +serviceName: {{ .serviceName }} +servicePort: {{ .servicePort }} +{{- else -}} +service: + name: {{ .serviceName }} + port: + {{- if typeIs "string" .servicePort }} + name: {{ .servicePort }} + {{- else if or (typeIs "int" .servicePort) (typeIs "float64" .servicePort) }} + number: {{ .servicePort | int }} + {{- end }} +{{- end -}} +{{- end -}} + +{{/* +Print "true" if the API pathType field is supported +Usage: +{{ include "relate.supportsPathType" . }} +*/}} +{{- define "relate.supportsPathType" -}} +{{- if (semverCompare "<1.18-0" (include "relate.kubeVersion" .)) -}} +{{- print "false" -}} +{{- else -}} +{{- print "true" -}} +{{- end -}} +{{- end -}} + +{{/* +Returns true if the ingressClassname field is supported +Usage: +{{ include "relate.supportsIngressClassname" . }} +*/}} +{{- define "relate.supportsIngressClassname" -}} +{{- if semverCompare "<1.18-0" (include "relate.kubeVersion" .) -}} +{{- print "false" -}} +{{- else -}} +{{- print "true" -}} +{{- end -}} +{{- end -}} diff --git a/helm/relate/templates/backup.yaml b/helm/relate/templates/backup.yaml new file mode 100644 index 000000000..e03b68fc4 --- /dev/null +++ b/helm/relate/templates/backup.yaml @@ -0,0 +1,10 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: ScheduledBackup +metadata: + name: relate-pg-backup # Name of the backup +spec: + immediate: true # Backup starts immediately after ScheduledBackup has been created + backupOwnerReference: self + schedule: "0 0 3 * * *" + cluster: + name: relate-pgsql # Cluster name \ No newline at end of file diff --git a/helm/relate/templates/configmap.yaml b/helm/relate/templates/configmap.yaml new file mode 100644 index 000000000..f420fb1c8 --- /dev/null +++ b/helm/relate/templates/configmap.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Release.Name }}-nginx-config +data: + nginx.conf: | + events { + worker_connections 10;} + http{ + server { + listen 8001; + server_name {{ .Values.nginx.serverName }}; + location /static/ { + root {{ .Values.nginx.rootDirectory }}; + autoindex on; + include mime.types; + try_files $uri $uri.html =404; + } + } + } diff --git a/helm/relate/templates/deployment.yaml b/helm/relate/templates/deployment.yaml new file mode 100644 index 000000000..a6fb58b8e --- /dev/null +++ b/helm/relate/templates/deployment.yaml @@ -0,0 +1,108 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "relate.fullname" . }} + namespace: {{ include "relate.namespace" . }} + labels: {{- include "relate.labels" . | nindent 4 }} +spec: + selector: + matchLabels: {{- include "relate.matchLabels" . | nindent 6 }} + template: + metadata: + {{- if .Values.podAnnotations }} + annotations: {{- toYaml .Values.podAnnotations | nindent 8 }} + {{- end }} + labels: {{- include "relate.matchLabels" . | nindent 8 }} + {{- if .Values.podLabels }} + {{- toYaml .Values.podLabels | nindent 8 }} + {{- end }} + spec: + {{- if .Values.image.pullSecret }} + imagePullSecrets: + - name: {{ .Values.image.pullSecret }} + {{- end }} + {{- if .Values.affinity }} + affinity: {{- toYaml .Values.affinity | nindent 8 }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: {{- toYaml .Values.nodeSelector | nindent 8 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: {{- toYaml .Values.tolerations | nindent 8 }} + {{- end }} + {{- if .Values.priorityClassName }} + priorityClassName: {{ .Values.priorityClassName | quote }} + {{- end }} + containers: + - name: nginx + image: {{ .Values.nginx.image | default "nginx:latest" }} + imagePullPolicy: {{ .Values.nginx.pullPolicy | default "IfNotPresent"}} # Default pull policy if not provided + volumeMounts: + - name: nginx-config-volume + mountPath: /etc/nginx/nginx.conf + subPath: nginx.conf + - name: shared-static-data + mountPath: /var/www/relate/static + ports: + - containerPort: 8001 + - name: relate + command: + - /run-relate.sh + image: {{ printf "%s/%s:%s" .Values.image.registry .Values.image.repository .Values.image.tag }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - containerPort: 8000 + name: http + livenessProbe: + httpGet: + path: / + port: http + httpHeaders: + - name: Host + value: relate + initialDelaySeconds: 10 + periodSeconds: 60 + timeoutSeconds: 300 + failureThreshold: 30 + successThreshold: 1 + readinessProbe: + httpGet: + path: / + port: http + httpHeaders: + - name: Host + value: relate + initialDelaySeconds: 10 + periodSeconds: 160 + timeoutSeconds: 300 + failureThreshold: 30 + successThreshold: 1 + {{- if .Values.resources }} + resources: {{- toYaml .Values.resources | nindent 12 }} + {{- end }} + {{- if .Values.environment }} + env: {{- toYaml .Values.environment | nindent 12 }} + - name: HOSTS + value: relate;{{ .Values.ingress.hostname }};{{ range .Values.ingress.extraHosts }}{{ .name }};{{ end }} + {{- end }} + volumeMounts: + - mountPath: /var/www/relate/git-roots + name: data + - name: shared-static-data + mountPath: /var/www/relate/static + + volumes: + - name: data + {{- if .Values.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ if .Values.persistence.existingClaim }}{{ .Values.persistence.existingClaim }}{{- else }}{{ template "relate.fullname" . }}{{- end }} + {{- else }} + emptyDir: {{- toYaml .Values.emptyDir | nindent 10 }} + {{- end }} + - name: shared-static-data + persistentVolumeClaim: + claimName: {{ .Release.Name }}-staticdata + - name: nginx-config-volume + configMap: + name: {{ .Release.Name }}-nginx-config + diff --git a/helm/relate/templates/ingress.yaml b/helm/relate/templates/ingress.yaml new file mode 100644 index 000000000..cf5beee51 --- /dev/null +++ b/helm/relate/templates/ingress.yaml @@ -0,0 +1,65 @@ +{{- if .Values.ingress.enabled }} +apiVersion: {{ include "relate.ingress.apiVersion" . }} +kind: Ingress +metadata: + name: {{ include "relate.fullname" . }} + namespace: {{ include "relate.namespace" . }} + labels: {{- include "relate.labels" . | nindent 4 }} + {{- if .Values.ingress.annotations }} + annotations: + {{- if .Values.ingress.annotations }} + {{- include "relate.render" ( dict "value" .Values.ingress.annotations "context" $) | nindent 4 }} + {{- end }} + {{- end }} +spec: + {{- if and .Values.ingress.ingressClassName (eq "true" (include "relate.supportsIngressClassname" .)) }} + ingressClassName: {{ .Values.ingress.ingressClassName | quote }} + {{- end }} + rules: + {{- if .Values.ingress.hostname }} + - host: {{ .Values.ingress.hostname }} + http: + paths: + {{- if .Values.ingress.extraPaths }} + {{- toYaml .Values.ingress.extraPaths | nindent 10 }} + {{- end }} + - path: /static/ + pathType: Prefix + backend: + service: + name: relate + port: + number: 8001 + - path: {{ .Values.ingress.path }} + {{- if eq "true" (include "relate.supportsPathType" .) }} + pathType: {{ .Values.ingress.pathType }} + {{- end }} + backend: {{- include "relate.backend" (dict "serviceName" (include "relate.fullname" .) "servicePort" "http" "context" $) | nindent 14 }} + {{- end }} + {{- range .Values.ingress.extraHosts }} + - host: {{ .name | quote }} + http: + paths: + - path: {{ default "/" .path }} + {{- if eq "true" (include "relate.supportsPathType" $) }} + pathType: {{ default "ImplementationSpecific" .pathType }} + {{- end }} + backend: {{- include "relate.backend" (dict "serviceName" (include "relate.fullname" $) "servicePort" "http" "context" $) | nindent 14 }} + {{- end }} + {{- if or (and .Values.ingress.tls (or (include "relate.ingress.certManagerRequest" .Values.ingress.annotations) .Values.ingress.selfSigned)) .Values.ingress.extraTls }} + tls: + {{- if and .Values.ingress.tls (or (include "relate.ingress.certManagerRequest" .Values.ingress.annotations) .Values.ingress.selfSigned) }} + - hosts: + - {{ .Values.ingress.hostname | quote }} + secretName: {{ printf "%s-tls" .Values.ingress.hostname }} + {{- range .Values.ingress.extraHosts }} + - hosts: + - {{ .name }} + secretName: {{ printf "%s-tls" .name }} + {{- end }} + {{- end }} + {{- if .Values.ingress.extraTls }} + {{- include "relate.render" (dict "value" .Values.ingress.extraTls "context" $) | nindent 4 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/helm/relate/templates/postgres.yaml b/helm/relate/templates/postgres.yaml new file mode 100644 index 000000000..a495dc583 --- /dev/null +++ b/helm/relate/templates/postgres.yaml @@ -0,0 +1,104 @@ +{{- if not (.Values.postgresql).enabled }} + +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + {{ if (.Values.cnpg).recover }} + name: relate-pgsql-recover + {{ else }} + name: relate-pgsql + {{ end }} +spec: + instances: {{ ((.Values.cnpg).instance) | default 1 }} + {{ if (.Values.cnpg).recover }} + bootstrap: + recovery: + source: relate-pgsql + database: relate + owner: relate + {{ else }} + bootstrap: + initdb: + database: relate + owner: relate + {{ end }} + resources: + limits: + memory: {{ (((.Values.cnpg).resources).limits).memory | default "800Mi" }} + requests: + memory: {{ (((.Values.cnpg).resources).requests).memory | default "500Mi" }} + cpu: {{ (((.Values.cnpg).resources).requests).cpu | default "100m" }} + storage: + size: {{ (.Values.cnpg).size | default "4Gi" }} + {{- if (.Values.cnpg).storageClass }} + storageClass: {{ (.Values.cnpg).storageClass }} + {{- end }} + {{- if (.Values.cnpg).recover }} + externalClusters: + - name: relate-pgsql + barmanObjectStore: + destinationPath: {{ ((.Values.cnpg).backup).destinationPath }} + endpointURL: {{ ((.Values.cnpg).backup).endpointURL }} + {{- if eq ((.Values.cnpg).backup).cloud "aws" }} + s3Credentials: + accessKeyId: + name: relate-secret + key: ACCESS_KEY_ID + secretAccessKey: + name: relate-secret + key: ACCESS_SECRET_KEY + {{- end }} + {{- if eq ((.Values.cnpg).backup).cloud "azure" }} + azureCredentials: + connectionString: + name: relate-pgsql-backup-creds + key: AZURE_CONNECTION_STRING + storageAccount: + name: relate-pgsql-backup-creds + key: AZURE_STORAGE_ACCOUNT + storageKey: + name: relate-pgsql-backup-creds + key: AZURE_STORAGE_KEY + storageSasToken: + name: relate-pgsql-backup-creds + key: AZURE_STORAGE_SAS_TOKEN + {{- end }} + wal: + maxParallel: 8 + {{- end }} + {{- if (.Values.cnpg).backup }} + backup: + barmanObjectStore: + destinationPath: {{ ((.Values.cnpg).backup).destinationPath }} + endpointURL: {{ ((.Values.cnpg).backup).endpointURL }} + {{- if eq ((.Values.cnpg).backup).cloud "aws" }} + s3Credentials: + accessKeyId: + name: relate-secret + key: ACCESS_KEY_ID + secretAccessKey: + name: relate-secret + key: ACCESS_SECRET_KEY + {{- end }} + {{- if eq ((.Values.cnpg).backup).cloud "azure" }} + azureCredentials: + connectionString: + name: relate-pgsql-backup-creds + key: AZURE_CONNECTION_STRING + storageAccount: + name: relate-pgsql-backup-creds + key: AZURE_STORAGE_ACCOUNT + storageKey: + name: relate-pgsql-backup-creds + key: AZURE_STORAGE_KEY + storageSasToken: + name: relate-pgsql-backup-creds + key: AZURE_STORAGE_SAS_TOKEN + {{- end }} + wal: + encryption: "" + data: + encryption: "" + retentionPolicy: "30d" + {{ end }} +{{ end }} diff --git a/helm/relate/templates/pvc.yaml b/helm/relate/templates/pvc.yaml new file mode 100644 index 000000000..6885a071f --- /dev/null +++ b/helm/relate/templates/pvc.yaml @@ -0,0 +1,45 @@ +{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: {{ template "relate.fullname" . }} + namespace: {{ include "relate.namespace" . }} + labels: + app: {{ template "relate.name" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +{{ if .Values.persistence.annotations}} + annotations: + {{- range $key, $value := .Values.persistence.annotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} +{{- end }} +spec: + accessModes: + - {{ .Values.persistence.accessMode | quote }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} +{{- if .Values.persistence.storageClass }} +{{- if (eq "-" .Values.persistence.storageClass) }} + storageClassName: "" +{{- else }} + storageClassName: "{{ .Values.persistence.storageClass }}" +{{- end }} +{{- end }} +{{- end }} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Release.Name }}-staticdata +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ default "300Mi" .Values.nginx.pvcStorage }} + {{- if .Values.nginx.storageClass }} + storageClassName: {{ .Values.nginx.storageClass }} + {{- end }} diff --git a/helm/relate/templates/rabbitmq.yaml b/helm/relate/templates/rabbitmq.yaml new file mode 100644 index 000000000..77eb7f469 --- /dev/null +++ b/helm/relate/templates/rabbitmq.yaml @@ -0,0 +1,12 @@ +apiVersion: rabbitmq.com/v1beta1 +kind: RabbitmqCluster +metadata: + name: rabbitmq +spec: + resources: + requests: + cpu: 500m + memory: 200Mi + limits: + cpu: 500m + memory: 400Mi diff --git a/helm/relate/templates/service.yaml b/helm/relate/templates/service.yaml new file mode 100644 index 000000000..6142060f3 --- /dev/null +++ b/helm/relate/templates/service.yaml @@ -0,0 +1,28 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "relate.fullname" . }} + namespace: {{ include "relate.namespace" . }} + labels: {{- include "relate.labels" . | nindent 4 }} + {{- if .Values.service.labels }} + {{- include "relate.render" ( dict "value" .Values.service.labels "context" $) | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.type }} + ports: + - name: http + port: {{ .Values.service.port }} + targetPort: http + {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePort)) }} + nodePort: {{ .Values.service.nodePort }} + {{- else if eq .Values.service.type "ClusterIP" }} + nodePort: null + {{- end }} + - name: nginx + port: {{ .Values.service.port }} + targetPort: 8001 + port: 8001 + selector: {{- include "relate.matchLabels" . | nindent 4 }} + + + diff --git a/helm/relate/values.yaml b/helm/relate/values.yaml new file mode 100644 index 000000000..525c82fb3 --- /dev/null +++ b/helm/relate/values.yaml @@ -0,0 +1,219 @@ +# nameOverride: "" + +## @param fullnameOverride String to fully override relate.fullname +## +# fullnameOverride: "" +## @param namespace Namespace where to deploy the RELATEs controller +## +namespace: "relate" + +## Relate image +## ref: https://quay.io/repository/bitnami/relate-controller?tab=tags +## @param image.registry RELATEs image registry +## @param image.repository RELATEs image repository +## @param image.tag RELATEs image tag (immutable tags are recommended) +## @param image.pullPolicy RELATEs image pull policy +## @param image.pullSecrets [array] RELATEs image pull secrets +## +image: + registry: registry.example.com + repository: obmondo/dockerfiles/relate + tag: latest + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: Always + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## e.g: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + #pullSecret: dockerfiles-pullsecrets +resources: + limits: {} + requests: {} + +persistence: + enabled: true + accessMode: ReadWriteOnce + size: 100Mi + storageClass: + +# default settings for emptydir volume +emptyDir: {} + +## Environment variables - db user+pass loaded from secret +environment: + - name: SMTPHOST + value: 'mail.system' + - name: SMTPPORT + value: '587' + - name: DBHOST + value: 'obmondo-postgresql' + - name: DBUSER + valueFrom: + secretKeyRef: + name: relate-admin.example-postgresql.credentials.postgresql.acid.zalan.do + key: username + - name: DBPASS + valueFrom: + secretKeyRef: + name: relate-admin.example-postgresql.credentials.postgresql.acid.zalan.do + key: password + +## @param podLabels [object] Extra labels for RELATE pods +## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ +## +podLabels: {} +## @param podAnnotations [object] Annotations for RELATE pods +## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ +## +podAnnotations: {} +## @param priorityClassName RELATE pods' priorityClassName +## +priorityClassName: "" +## @param affinity [object] Affinity for RELATE pods assignment +## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity +## +affinity: {} +## @param nodeSelector [object] Node labels for RELATE pods assignment +## ref: https://kubernetes.io/docs/user-guide/node-selection/ +## +nodeSelector: {} +## @param tolerations [array] Tolerations for RELATE pods assignment +## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +## +tolerations: [] + +## @section Traffic Exposure Parameters + +## +service: + ## @param service.type RELATE service type + ## + type: ClusterIP + ## @param service.port RELATE service HTTP port + ## + port: 8000 + ## @param service.nodePort Node port for HTTP + ## Specify the nodePort value for the LoadBalancer and NodePort service types + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## NOTE: choose port between <30000-32767> + ## + nodePort: "" + ## @param service.annotations [object] Additional custom annotations for RELATE service + ## + annotations: {} +## RELATE ingress parameters +## ref: http://kubernetes.io/docs/user-guide/ingress/ +## +ingress: + ## @param ingress.enabled Enable ingress record generation for RELATE + ## + enabled: false + ## @param ingress.pathType Ingress path type + ## + pathType: ImplementationSpecific + ## @param ingress.apiVersion Force Ingress API version (automatically detected if not set) + ## + # apiVersion: "" + ## @param ingress.ingressClassName IngressClass that will be be used to implement the Ingress + ## This is supported in Kubernetes 1.18+ and required if you have more than one IngressClass marked as the default for your cluster. + ## ref: https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/ + ## + # ingressClassName: "" + ## @param ingress.hostname Default host for the ingress record + ## + hostname: relate.example.com + ## @param ingress.annotations [object] Additional annotations for the Ingress resource. To enable certificate autogeneration, place here your cert-manager annotations. + ## Use this parameter to set the required annotations for cert-manager, see + ## ref: https://cert-manager.io/docs/usage/ingress/#supported-annotations + ## e.g: + ## annotations: + ## kubernetes.io/ingress.class: nginx + ## cert-manager.io/cluster-issuer: cluster-issuer-name + ## + annotations: + cert-manager.io/cluster-issuer: letsencrypt + kubernetes.io/ingress.class: traefik-cert-manager + ## @param ingress.tls Enable TLS configuration for the host defined at `ingress.hostname` parameter + ## TLS certificates will be retrieved from a TLS secret with name: `{{- printf "%s-tls" .Values.ingress.hostname }}` + ## You can: + ## - Use the `ingress.secrets` parameter to create this TLS secret + ## - Relay on cert-manager to create it by setting the corresponding annotations + ## - Relay on Helm to create self-signed certificates by setting `ingress.selfSigned=true` + ## + tls: true + ## @param ingress.selfSigned Create a TLS secret for this ingress record using self-signed certificates generated by Helm + ## + selfSigned: false + ## @param ingress.extraHosts [array] An array with additional hostname(s) to be covered with the ingress record + ## e.g: + ## extraHosts: + ## - name: relate.local + ## path: / + ## + extraHosts: + - name: relate.example.com + path: / + +# @param ingress.extraPaths [array] An array with additional arbitrary paths that may need to be added to the ingress under the main host +# e.g: +# extraPaths: +# - path: /* +# backend: +# serviceName: ssl-redirect +# servicePort: use-annotation + +# extraPaths: [ ] +# @param ingress.extraTls [array] TLS configuration for additional hostname(s) to be covered with this ingress record +# ref: https://kubernetes.io/docs/concepts/services-networking/ingress/#tls +# e.g: +# extraTls: +# - hosts: +# - relate.local +# secretName: relate.local-tls + +# extraTls: [ ] +# @param ingress.secrets [array] Custom TLS certificates as secrets +# NOTE: 'key' and 'certificate' are expected in PEM format +# NOTE: 'name' should line up with a 'secretName' set further up +# If it is not set and you're using cert-manager, this is unneeded, as it will create a secret for you with valid certificates +# If it is not set and you're NOT using cert-manager either, self-signed certificates will be created valid for 365 days +# It is also possible to create and manage the certificates outside of this helm chart +# Please see README.md for more information +# e.g: +# secrets: +# - name: relate.local-tls +# key: |- +# -----BEGIN RSA PRIVATE KEY----- +# ... +# -----END RSA PRIVATE KEY----- +# certificate: |- +# -----BEGIN CERTIFICATE----- +# ... +# -----END CERTIFICATE----- + +networkPolicy: + ## @param networkPolicy.enabled Specifies whether a NetworkPolicy should be created + ## + enabled: false + +## @section Other Parameters + +## RBAC configuration +## +rbac: + ## @param rbac.create Specifies whether RBAC resources should be created + ## + create: true + ## @param rbac.labels Extra labels to be added to RBAC resources + ## + labels: {} + ## @param rbac.pspEnabled PodSecurityPolicy + ## + pspEnabled: false +