diff --git a/5.2/5.2.7/Dockerfile.python38 b/5.2/5.2.7/Dockerfile.python38 new file mode 100644 index 0000000..0892385 --- /dev/null +++ b/5.2/5.2.7/Dockerfile.python38 @@ -0,0 +1,59 @@ +FROM python:3.8-slim-buster as base +FROM base as builder + +ENV PIP_PARAMS="--use-deprecated legacy-resolver" +ENV PLONE_VERSION=5.2.7 +ENV PLONE_VOLTO="plone.volto==3.1.0a4" +ENV EXTRA_PACKAGES="relstorage==3.4.5 psycopg2==2.9.3 python-ldap==3.4.0" + +RUN mkdir /wheelhouse + +RUN apt-get update \ + && buildDeps="dpkg-dev gcc libbz2-dev libc6-dev libffi-dev libjpeg62-turbo-dev libldap2-dev libopenjp2-7-dev libpcre3-dev libpq-dev libsasl2-dev libssl-dev libtiff5-dev libxml2-dev libxslt1-dev wget zlib1g-dev python3-dev build-essential" \ + && apt-get install -y --no-install-recommends $buildDeps\ + && rm -rf /var/lib/apt/lists/* /usr/share/doc + +RUN pip wheel Paste Plone ${PLONE_VOLTO} ${EXTRA_PACKAGES} -c https://dist.plone.org/release/$PLONE_VERSION/constraints.txt ${PIP_PARAMS} --wheel-dir=/wheelhouse + +FROM base + +ENV PIP_PARAMS="--use-deprecated legacy-resolver" +ENV PIP_VERSION=21.3 + +LABEL maintainer="Plone Community " \ + org.label-schema.name="plone-backend" \ + org.label-schema.description="Plone backend image image using Python 3.8" \ + org.label-schema.vendor="Plone Foundation" \ + org.label-schema.docker.cmd="docker run -d -p 8080:8080 plone/plone-backend:5.2.6-python38" + +COPY --from=builder /wheelhouse /wheelhouse + +RUN useradd --system -m -d /app -U -u 500 plone \ + && runDeps="git libjpeg62 libopenjp2-7 libpq5 libtiff5 libxml2 libxslt1.1 lynx poppler-utils rsync wv busybox gosu" \ + && apt-get update \ + && apt-get install -y --no-install-recommends $runDeps \ + && busybox --install -s \ + && rm -rf /var/lib/apt/lists/* /usr/share/doc \ + && mkdir -p /data/filestorage /data/blobstorage /data/log /data/cache + +WORKDIR /app + +RUN python -m venv . \ + && ./bin/pip install -U "pip==${PIP_VERSION}" \ + && ./bin/pip install --force-reinstall --no-index --no-deps ${PIP_PARAMS} /wheelhouse/* \ + && find . \( -type f -a -name '*.pyc' -o -name '*.pyo' \) -exec rm -rf '{}' + \ + && rm -rf .cache + +COPY skeleton/ /app + +RUN ln -s /data var \ + && find /data -not -user plone -exec chown plone:plone {} \+ \ + && find /app -not -user plone -exec chown plone:plone {} \+ + +EXPOSE 8080 +VOLUME /data + +HEALTHCHECK --interval=10s --timeout=5s --start-period=30s CMD wget -q http://127.0.0.1:8080/ok -O - | grep OK || exit 1 + +ENTRYPOINT [ "/app/docker-entrypoint.sh" ] +CMD ["start"] diff --git a/5.2/5.2.7/Makefile b/5.2/5.2.7/Makefile new file mode 100644 index 0000000..ebad8ce --- /dev/null +++ b/5.2/5.2.7/Makefile @@ -0,0 +1,56 @@ +### Defensive settings for make: +# https://tech.davis-hansson.com/p/make/ +SHELL:=bash +.ONESHELL: +.SHELLFLAGS:=-xeu -o pipefail -O inherit_errexit -c +.SILENT: +.DELETE_ON_ERROR: +MAKEFLAGS+=--warn-undefined-variables +MAKEFLAGS+=--no-builtin-rules + +IMAGE_NAME=plone/plone-backend +IMAGE_TAG=5.2.6 +IMAGE_VARIATIONS=python36 python37 python38 +IMAGE_DEFAULT_VARIATION=python38 +IMAGE_ADDITIONAL_TAGS=5.2 latest + +# We like colors +# From: https://coderwall.com/p/izxssa/colored-makefile-for-golang-projects +RED=`tput setaf 1` +GREEN=`tput setaf 2` +RESET=`tput sgr0` +YELLOW=`tput setaf 3` + +.PHONY: all +all: build-images + +# Add the following 'help' target to your Makefile +# And add help text after each target name starting with '\#\#' +.PHONY: help +help: ## This help message + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +.PHONY: build-images +build-images: ## Build Docker Images + @echo "Building $(IMAGE_NAME):$(IMAGE_TAG)" + $(foreach VARIATION,$(IMAGE_VARIATIONS),docker build . -t $(IMAGE_NAME):$(IMAGE_TAG)-${VARIATION} -f Dockerfile.${VARIATION};) + +.PHONY: push-images +tag-images: ## Tag images + @echo "Tag $(IMAGE_NAME):$(IMAGE_TAG)-${IMAGE_DEFAULT_VARIATION} as $(IMAGE_NAME):$(IMAGE_TAG)" + @docker tag $(IMAGE_NAME):$(IMAGE_TAG)-${IMAGE_DEFAULT_VARIATION} $(IMAGE_NAME):$(IMAGE_TAG) + @echo "Tag $(IMAGE_NAME):$(IMAGE_TAG) with additional tags, if any" + $(foreach TAG,$(IMAGE_ADDITIONAL_TAGS),docker tag $(IMAGE_NAME):$(IMAGE_TAG) $(IMAGE_NAME):$(TAG);) + +.PHONY: push-images +push-images: ## Push docker image to dockerhub + @echo "Push $(IMAGE_NAME):$(IMAGE_TAG) to Docker Hub" + @docker push $(IMAGE_NAME):$(IMAGE_TAG) + @echo "Push $(IMAGE_NAME):$(IMAGE_TAG) additional tags to Docker Hub" + $(foreach TAG,$(IMAGE_ADDITIONAL_TAGS),docker push $(IMAGE_NAME):$(TAG);) + @echo "Push all $(IMAGE_NAME):$(IMAGE_TAG) variations to Docker Hub" + $(foreach VARIATION,$(IMAGE_VARIATIONS),docker push $(IMAGE_NAME):$(IMAGE_TAG)-${VARIATION};) + +.PHONY: release +release: build-images push-images ## Build and push the image to docker hub + @echo "Releasing $(IMAGE_NAME):$(IMAGE_TAG)" diff --git a/5.2/5.2.7/skeleton/docker-entrypoint.sh b/5.2/5.2.7/skeleton/docker-entrypoint.sh new file mode 100755 index 0000000..d43aed1 --- /dev/null +++ b/5.2/5.2.7/skeleton/docker-entrypoint.sh @@ -0,0 +1,113 @@ +#!/bin/bash +set -e + +if [ -z "${PIP_PARAMS}" ]; then + PIP_PARAMS="--use-deprecated legacy-resolver" +fi + +# CLIENT HOME +CLIENT_HOME="/data/$(hostname)/$(hostid)" +export CLIENT_HOME=$CLIENT_HOME + +USER="$(id -u)" + +# Create directories to be used by Plone +mkdir -p /data/filestorage /data/blobstorage /data/cache /data/log $CLIENT_HOME +if [ "$USER" = '0' ]; then + find /data -not -user plone -exec chown plone:plone {} \+ + sudo="gosu plone" +else + sudo="" +fi + +# MAIN ENV Vars +[ -z ${SECURITY_POLICY_IMPLEMENTATION+x} ] && export SECURITY_POLICY_IMPLEMENTATION=C +[ -z ${VERBOSE_SECURITY+x} ] && export VERBOSE_SECURITY=off +[ -z ${DEFAULT_ZPUBLISHER_ENCODING+x} ] && export DEFAULT_ZPUBLISHER_ENCODING=utf-8 +[ -z ${DEBUG_MODE+x} ] && export DEBUG_MODE=off + +# ZODB ENV Vars +[ -z ${ZODB_CACHE_SIZE+x} ] && export ZODB_CACHE_SIZE=50000 + +if [[ -v RELSTORAGE_DSN ]]; then + MSG="Using Relstorage configuration" + CONF=relstorage.conf + # Relstorage ENV Vars + [ -z ${RELSTORAGE_NAME+x} ] && export RELSTORAGE_NAME=storage + [ -z ${RELSTORAGE_READ_ONLY+x} ] && export RELSTORAGE_READ_ONLY=off + [ -z ${RELSTORAGE_KEEP_HISTORY+x} ] && export RELSTORAGE_KEEP_HISTORY=true + [ -z ${RELSTORAGE_COMMIT_LOCK_TIMEOUT+x} ] && export RELSTORAGE_COMMIT_LOCK_TIMEOUT=30 + [ -z ${RELSTORAGE_CREATE_SCHEMA+x} ] && export RELSTORAGE_CREATE_SCHEMA=true + [ -z ${RELSTORAGE_SHARED_BLOB_DIR+x} ] && export RELSTORAGE_SHARED_BLOB_DIR=false + [ -z ${RELSTORAGE_BLOB_CACHE_SIZE+x} ] && export RELSTORAGE_BLOB_CACHE_SIZE=100mb + [ -z ${RELSTORAGE_BLOB_CACHE_SIZE_CHECK+x} ] && export RELSTORAGE_BLOB_CACHE_SIZE_CHECK=10 + [ -z ${RELSTORAGE_BLOB_CACHE_SIZE_CHECK_EXTERNAL+x} ] && export RELSTORAGE_BLOB_CACHE_SIZE_CHECK_EXTERNAL=false + [ -z ${RELSTORAGE_BLOB_CHUNK_SIZE+x} ] && export RELSTORAGE_BLOB_CHUNK_SIZE=1048576 + [ -z ${RELSTORAGE_CACHE_LOCAL_MB+x} ] && export RELSTORAGE_CACHE_LOCAL_MB=10 + [ -z ${RELSTORAGE_CACHE_LOCAL_OBJECT_MAX+x} ] && export RELSTORAGE_CACHE_LOCAL_OBJECT_MAX=16384 + [ -z ${RELSTORAGE_CACHE_LOCAL_COMPRESSION+x} ] && export RELSTORAGE_CACHE_LOCAL_COMPRESSION=none + [ -z ${RELSTORAGE_CACHE_DELTA_SIZE_LIMIT+x} ] && export RELSTORAGE_CACHE_DELTA_SIZE_LIMIT=100000 +elif [[ -v ZEO_ADDRESS ]]; then + MSG="Using ZEO configuration" + CONF=zeo.conf + # Check ZEO variables + [ -z ${ZEO_SHARED_BLOB_DIR+x} ] && export ZEO_SHARED_BLOB_DIR=off + [ -z ${ZEO_READ_ONLY+x} ] && export ZEO_READ_ONLY=false + [ -z ${ZEO_CLIENT_READ_ONLY_FALLBACK+x} ] && export ZEO_CLIENT_READ_ONLY_FALLBACK=false + [ -z ${ZEO_STORAGE+x} ] && export ZEO_STORAGE=1 + [ -z ${ZEO_CLIENT_CACHE_SIZE+x} ] && export ZEO_CLIENT_CACHE_SIZE=128MB + [ -z ${ZEO_DROP_CACHE_RATHER_VERIFY+x} ] && export ZEO_DROP_CACHE_RATHER_VERIFY=false +else + MSG="Using default configuration" + CONF=zope.conf +fi + +# Handle ADDONS installation +if [[ -v ADDONS ]]; then + echo "=======================================================================================" + echo "Installing ADDONS ${ADDONS}" + echo "THIS IS NOT MEANT TO BE USED IN PRODUCTION" + echo "Read about it: https://github.com/plone/plone-backend/#extending-from-this-image" + echo "=======================================================================================" + /app/bin/pip install ${ADDONS} ${PIP_PARAMS} +fi + +# Handle development addons +if [[ -v DEVELOP ]]; then + echo "=======================================================================================" + echo "Installing DEVELOPment addons ${DEVELOP}" + echo "THIS IS NOT MEANT TO BE USED IN PRODUCTION" + echo "Read about it: https://github.com/plone/plone-backend/#extending-from-this-image" + echo "=======================================================================================" + /app/bin/pip install --editable ${DEVELOP} ${PIP_PARAMS} +fi + +# Handle Site creation +if [[ -v SITE ]]; then + export TYPE=${TYPE:-volto} + echo "=======================================================================================" + echo "Creating Plone ${TYPE} SITE: ${SITE}" + echo "Aditional profiles: ${PROFILES}" + echo "THIS IS NOT MEANT TO BE USED IN PRODUCTION" + echo "Read about it: https://github.com/plone/plone-backend/#extending-from-this-image" + echo "=======================================================================================" + export SITE_ID=${SITE} + $sudo /app/bin/zconsole run etc/${CONF} /app/scripts/create_site.py +fi + +if [[ "$1" == "start" ]]; then + echo $MSG + exec $sudo /app/bin/runwsgi -v etc/zope.ini config_file=${CONF} +elif [[ "$1" == "create-classic" ]]; then + export TYPE=classic + exec $sudo /app/bin/zconsole run etc/${CONF} /app/scripts/create_site.py +elif [[ "$1" == "create-volto" ]]; then + export TYPE=volto + exec $sudo /app/bin/zconsole run etc/${CONF} /app/scripts/create_site.py +elif [[ "$1" == "create-site" ]]; then + export TYPE=volto + exec $sudo /app/bin/zconsole run etc/${CONF} /app/scripts/create_site.py +else + # Custom + exec "$@" +fi diff --git a/5.2/5.2.7/skeleton/etc/relstorage.conf b/5.2/5.2.7/skeleton/etc/relstorage.conf new file mode 100644 index 0000000..da07f1f --- /dev/null +++ b/5.2/5.2.7/skeleton/etc/relstorage.conf @@ -0,0 +1,38 @@ +%define INSTANCE /app +instancehome $INSTANCE + +%define CLIENTHOME $(CLIENT_HOME) +clienthome $CLIENTHOME + +debug-mode $(DEBUG_MODE) +security-policy-implementation $(SECURITY_POLICY_IMPLEMENTATION) +verbose-security $(VERBOSE_SECURITY) +default-zpublisher-encoding $(DEFAULT_ZPUBLISHER_ENCODING) + + + zope_i18n_compile_mo_files true + CHAMELEON_CACHE $INSTANCE/var/cache + + + + # Main database + cache-size $(ZODB_CACHE_SIZE) + %import relstorage + + name $(RELSTORAGE_NAME) + read-only $(RELSTORAGE_READ_ONLY) + keep-history $(RELSTORAGE_KEEP_HISTORY) + commit-lock-timeout $(RELSTORAGE_COMMIT_LOCK_TIMEOUT) + create-schema $(RELSTORAGE_CREATE_SCHEMA) + blob-dir $INSTANCE/var/blobstorage + shared-blob-dir $(RELSTORAGE_SHARED_BLOB_DIR) + blob-cache-size $(RELSTORAGE_BLOB_CACHE_SIZE) + blob-cache-size-check $(RELSTORAGE_BLOB_CACHE_SIZE_CHECK) + blob-cache-size-check-external $(RELSTORAGE_BLOB_CACHE_SIZE_CHECK_EXTERNAL) + blob-chunk-size $(RELSTORAGE_BLOB_CHUNK_SIZE) + + dsn $(RELSTORAGE_DSN) + + + mount-point / + diff --git a/5.2/5.2.7/skeleton/etc/site.zcml b/5.2/5.2.7/skeleton/etc/site.zcml new file mode 100644 index 0000000..0eeb830 --- /dev/null +++ b/5.2/5.2.7/skeleton/etc/site.zcml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/5.2/5.2.7/skeleton/etc/zeo.conf b/5.2/5.2.7/skeleton/etc/zeo.conf new file mode 100644 index 0000000..1346e68 --- /dev/null +++ b/5.2/5.2.7/skeleton/etc/zeo.conf @@ -0,0 +1,33 @@ +%define INSTANCE /app +instancehome $INSTANCE + +%define CLIENTHOME $(CLIENT_HOME) +clienthome $CLIENTHOME + +debug-mode $(DEBUG_MODE) +security-policy-implementation $(SECURITY_POLICY_IMPLEMENTATION) +verbose-security $(VERBOSE_SECURITY) +default-zpublisher-encoding $(DEFAULT_ZPUBLISHER_ENCODING) + + + zope_i18n_compile_mo_files true + CHAMELEON_CACHE $INSTANCE/var/cache + + + + # Main database + cache-size $(ZODB_CACHE_SIZE) + + name zeostorage + var $INSTANCE/var + blob-dir $INSTANCE/var/blobstorage + read-only $(ZEO_READ_ONLY) + read-only-fallback $(ZEO_CLIENT_READ_ONLY_FALLBACK) + shared-blob-dir $(ZEO_SHARED_BLOB_DIR) + server $(ZEO_ADDRESS) + storage $(ZEO_STORAGE) + cache-size $(ZEO_CLIENT_CACHE_SIZE) + drop-cache-rather-verify $(ZEO_DROP_CACHE_RATHER_VERIFY) + + mount-point / + diff --git a/5.2/5.2.7/skeleton/etc/zope.conf b/5.2/5.2.7/skeleton/etc/zope.conf new file mode 100644 index 0000000..b0ed95e --- /dev/null +++ b/5.2/5.2.7/skeleton/etc/zope.conf @@ -0,0 +1,26 @@ +%define INSTANCE /app +instancehome $INSTANCE + +debug-mode $(DEBUG_MODE) +security-policy-implementation $(SECURITY_POLICY_IMPLEMENTATION) +verbose-security $(VERBOSE_SECURITY) +default-zpublisher-encoding $(DEFAULT_ZPUBLISHER_ENCODING) + + + zope_i18n_compile_mo_files true + CHAMELEON_CACHE $INSTANCE/var/cache + + + + # Main database + cache-size $(ZODB_CACHE_SIZE) + # Blob-enabled FileStorage database + + blob-dir $INSTANCE/var/blobstorage + # FileStorage database + + path $INSTANCE/var/filestorage/Data.fs + + + mount-point / + diff --git a/5.2/5.2.7/skeleton/etc/zope.ini b/5.2/5.2.7/skeleton/etc/zope.ini new file mode 100644 index 0000000..8ef8eaf --- /dev/null +++ b/5.2/5.2.7/skeleton/etc/zope.ini @@ -0,0 +1,77 @@ +[app:zope] +use = egg:Zope#main +zope_conf = %(here)s/%(config_file)s + +[server:main] +use = egg:waitress#main +host = 0.0.0.0 +port = 8080 +threads = 4 +clear_untrusted_proxy_headers = false +max_request_body_size = 1073741824 + + +[filter:translogger] +use = egg:Paste#translogger +setup_console_handler = False + +[pipeline:main] +pipeline = + egg:Zope#httpexceptions + translogger + zope + +[loggers] +keys = root, waitress.queue, waitress, wsgi + +[handlers] +keys = console, accesslog, eventlog + +[formatters] +keys = generic, message + +[formatter_generic] +format = %(asctime)s %(levelname)s [%(name)s:%(lineno)s][%(threadName)s] %(message)s +datefmt = %Y-%m-%d %H:%M:%S + +[formatter_message] +format = %(message)s + +[logger_root] +level = INFO +handlers = console, eventlog + +[logger_waitress.queue] +level = INFO +handlers = eventlog +qualname = waitress.queue +propagate = 0 + +[logger_waitress] +level = INFO +handlers = eventlog +qualname = waitress + +[logger_wsgi] +level = WARN +handlers = accesslog +qualname = wsgi +propagate = 0 + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[handler_accesslog] +class = StreamHandler +args = (sys.stdout,) +level = INFO +formatter = message + +[handler_eventlog] +class = StreamHandler +args = (sys.stderr,) +level = INFO +formatter = generic diff --git a/5.2/5.2.7/skeleton/inituser b/5.2/5.2.7/skeleton/inituser new file mode 100644 index 0000000..bb09481 --- /dev/null +++ b/5.2/5.2.7/skeleton/inituser @@ -0,0 +1 @@ +admin:{SHA}0DPiKuNIrrVmD8IUCuw1hQxNqZc= \ No newline at end of file diff --git a/5.2/5.2.7/skeleton/scripts/create_site.py b/5.2/5.2.7/skeleton/scripts/create_site.py new file mode 100644 index 0000000..d530d26 --- /dev/null +++ b/5.2/5.2.7/skeleton/scripts/create_site.py @@ -0,0 +1,86 @@ +from AccessControl.SecurityManagement import newSecurityManager +from Products.CMFPlone.factory import _DEFAULT_PROFILE +from Products.CMFPlone.factory import addPloneSite +from Testing.makerequest import makerequest + +import transaction +import os + + +truthy = frozenset(('t', 'true', 'y', 'yes', 'on', '1')) + + +def asbool(s): + """Return the boolean value ``True`` if the case-lowered value of string + input ``s`` is a :term:`truthy string`. If ``s`` is already one of the + boolean values ``True`` or ``False``, return it.""" + if s is None: + return False + if isinstance(s, bool): + return s + s = str(s).strip() + return s.lower() in truthy + + +app = makerequest(app) + +request = app.REQUEST + +admin = app.acl_users.getUserById("admin") +admin = admin.__of__(app.acl_users) +newSecurityManager(None, admin) + +# VARS +TYPE = os.getenv("TYPE", "volto") +SITE_ID = os.getenv("SITE_ID", "Plone") +SETUP_CONTENT = asbool(os.getenv("SETUP_CONTENT")) +DELETE_EXISTING = asbool(os.getenv("DELETE_EXISTING")) +LANGUAGE = os.getenv("LANGUAGE", "en") +TIMEZONE = os.getenv("TIMEZONE", "Europe/Berlin") +ADDITIONAL_PROFILES = os.getenv("PROFILES", os.getenv("ADDITIONAL_PROFILES", "")) + +PROFILES = { + "volto": [ + "plone.app.caching:default", + "plonetheme.barceloneta:default", + "plone.volto:default", + "plone.volto:default-homepage", + ], + "classic": [ + "plone.app.caching:default", + "plonetheme.barceloneta:default", + ], +} + + +def profile_ids(site_type): + extension_ids = PROFILES[site_type] + if ADDITIONAL_PROFILES: + extension_ids.extend( + [ + profile.strip() + for profile in ADDITIONAL_PROFILES.split(" ") + if profile.strip() + ] + ) + return extension_ids + + +payload = { + "title": "Plone", + "profile_id": _DEFAULT_PROFILE, + "extension_ids": profile_ids(TYPE), + "setup_content": SETUP_CONTENT, + "default_language": LANGUAGE, + "portal_timezone": TIMEZONE, +} + +if SITE_ID in app.objectIds() and DELETE_EXISTING: + app.manage_delObjects([SITE_ID]) + transaction.commit() + app._p_jar.sync() + +if SITE_ID not in app.objectIds(): + site = addPloneSite(app, SITE_ID, **payload) + transaction.commit() + app._p_jar.sync() diff --git a/6.0/6.0.0a3/Dockerfile.python39 b/6.0/6.0.0a3/Dockerfile.python39 new file mode 100644 index 0000000..87f41d4 --- /dev/null +++ b/6.0/6.0.0a3/Dockerfile.python39 @@ -0,0 +1,58 @@ +FROM python:3.9-slim-bullseye as base +FROM base as builder + +ENV PIP_PARAMS="--use-deprecated legacy-resolver" +ENV PLONE_VERSION=6.0.0a3 +ENV EXTRA_PACKAGES="relstorage==3.4.5 psycopg2==2.9.3 python-ldap==3.4.0" + +RUN mkdir /wheelhouse + +RUN apt-get update \ + && buildDeps="dpkg-dev gcc libbz2-dev libc6-dev libffi-dev libjpeg62-turbo-dev libldap2-dev libopenjp2-7-dev libpcre3-dev libpq-dev libsasl2-dev libssl-dev libtiff5-dev libxml2-dev libxslt1-dev wget zlib1g-dev python3-dev build-essential" \ + && apt-get install -y --no-install-recommends $buildDeps\ + && rm -rf /var/lib/apt/lists/* /usr/share/doc + +RUN pip wheel Plone plone.volto ${EXTRA_PACKAGES} -c https://dist.plone.org/release/$PLONE_VERSION/constraints.txt ${PIP_PARAMS} --wheel-dir=/wheelhouse + +FROM base + +ENV PIP_PARAMS="--use-deprecated legacy-resolver" +ENV PIP_VERSION=21.3 + +LABEL maintainer="Plone Community " \ + org.label-schema.name="plone-backend" \ + org.label-schema.description="Plone backend image image using Python 3.9" \ + org.label-schema.vendor="Plone Foundation" \ + org.label-schema.docker.cmd="docker run -d -p 8080:8080 plone/plone-backend:6.0.0a2-python39" + +COPY --from=builder /wheelhouse /wheelhouse + +RUN useradd --system -m -d /app -U -u 500 plone \ + && runDeps="git libjpeg62 libopenjp2-7 libpq5 libtiff5 libxml2 libxslt1.1 lynx poppler-utils rsync wv busybox gosu make" \ + && apt-get update \ + && apt-get install -y --no-install-recommends $runDeps \ + && busybox --install -s \ + && rm -rf /var/lib/apt/lists/* /usr/share/doc \ + && mkdir -p /data/filestorage /data/blobstorage /data/log /data/cache + +WORKDIR /app + +RUN python -m venv . \ + && ./bin/pip install -U "pip==${PIP_VERSION}" \ + && ./bin/pip install --force-reinstall --no-index --no-deps ${PIP_PARAMS} /wheelhouse/* \ + && find . \( -type f -a -name '*.pyc' -o -name '*.pyo' \) -exec rm -rf '{}' + \ + && rm -rf .cache + +COPY skeleton/ /app + +RUN ln -s /data var \ + && find /data -not -user plone -exec chown plone:plone {} \+ \ + && find /app -not -user plone -exec chown plone:plone {} \+ + +EXPOSE 8080 +VOLUME /data + +HEALTHCHECK --interval=10s --timeout=5s --start-period=30s CMD wget -q http://127.0.0.1:8080/ok -O - | grep OK || exit 1 + +ENTRYPOINT [ "/app/docker-entrypoint.sh" ] +CMD ["start"] diff --git a/6.0/6.0.0a3/Makefile b/6.0/6.0.0a3/Makefile new file mode 100644 index 0000000..3b23b5a --- /dev/null +++ b/6.0/6.0.0a3/Makefile @@ -0,0 +1,56 @@ +### Defensive settings for make: +# https://tech.davis-hansson.com/p/make/ +SHELL:=bash +.ONESHELL: +.SHELLFLAGS:=-xeu -o pipefail -O inherit_errexit -c +.SILENT: +.DELETE_ON_ERROR: +MAKEFLAGS+=--warn-undefined-variables +MAKEFLAGS+=--no-builtin-rules + +IMAGE_NAME=plone/plone-backend +IMAGE_TAG=6.0.0a2 +IMAGE_VARIATIONS=python39 python38 python37 +IMAGE_DEFAULT_VARIATION=python39 +IMAGE_ADDITIONAL_TAGS= + +# We like colors +# From: https://coderwall.com/p/izxssa/colored-makefile-for-golang-projects +RED=`tput setaf 1` +GREEN=`tput setaf 2` +RESET=`tput sgr0` +YELLOW=`tput setaf 3` + +.PHONY: all +all: build-images + +# Add the following 'help' target to your Makefile +# And add help text after each target name starting with '\#\#' +.PHONY: help +help: ## This help message + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +.PHONY: build-images +build-images: ## Build Docker Images + @echo "Building $(IMAGE_NAME):$(IMAGE_TAG)" + $(foreach VARIATION,$(IMAGE_VARIATIONS),docker build . -t $(IMAGE_NAME):$(IMAGE_TAG)-${VARIATION} -f Dockerfile.${VARIATION};) + +.PHONY: tag-images +tag-images: ## Tag images + @echo "Tag $(IMAGE_NAME):$(IMAGE_TAG)-${IMAGE_DEFAULT_VARIATION} as $(IMAGE_NAME):$(IMAGE_TAG)" + @docker tag $(IMAGE_NAME):$(IMAGE_TAG)-${IMAGE_DEFAULT_VARIATION} $(IMAGE_NAME):$(IMAGE_TAG) + @echo "Tag $(IMAGE_NAME):$(IMAGE_TAG) with additional tags, if any" + $(foreach TAG,$(IMAGE_ADDITIONAL_TAGS),docker tag $(IMAGE_NAME):$(IMAGE_TAG) $(IMAGE_NAME):$(TAG);) + +.PHONY: push-images +push-images: ## Push docker image to dockerhub + @echo "Push $(IMAGE_NAME):$(IMAGE_TAG) to Docker Hub" + @docker push $(IMAGE_NAME):$(IMAGE_TAG) + @echo "Push $(IMAGE_NAME):$(IMAGE_TAG) additional tags to Docker Hub" + $(foreach TAG,$(IMAGE_ADDITIONAL_TAGS),docker push $(IMAGE_NAME):$(TAG);) + @echo "Push all $(IMAGE_NAME):$(IMAGE_TAG) variations to Docker Hub" + $(foreach VARIATION,$(IMAGE_VARIATIONS),docker push $(IMAGE_NAME):$(IMAGE_TAG)-${VARIATION};) + +.PHONY: release +release: build-images push-images ## Build and push the image to docker hub + @echo "Releasing $(IMAGE_NAME):$(IMAGE_TAG)" diff --git a/6.0/6.0.0a3/skeleton/docker-entrypoint.sh b/6.0/6.0.0a3/skeleton/docker-entrypoint.sh new file mode 100755 index 0000000..d43aed1 --- /dev/null +++ b/6.0/6.0.0a3/skeleton/docker-entrypoint.sh @@ -0,0 +1,113 @@ +#!/bin/bash +set -e + +if [ -z "${PIP_PARAMS}" ]; then + PIP_PARAMS="--use-deprecated legacy-resolver" +fi + +# CLIENT HOME +CLIENT_HOME="/data/$(hostname)/$(hostid)" +export CLIENT_HOME=$CLIENT_HOME + +USER="$(id -u)" + +# Create directories to be used by Plone +mkdir -p /data/filestorage /data/blobstorage /data/cache /data/log $CLIENT_HOME +if [ "$USER" = '0' ]; then + find /data -not -user plone -exec chown plone:plone {} \+ + sudo="gosu plone" +else + sudo="" +fi + +# MAIN ENV Vars +[ -z ${SECURITY_POLICY_IMPLEMENTATION+x} ] && export SECURITY_POLICY_IMPLEMENTATION=C +[ -z ${VERBOSE_SECURITY+x} ] && export VERBOSE_SECURITY=off +[ -z ${DEFAULT_ZPUBLISHER_ENCODING+x} ] && export DEFAULT_ZPUBLISHER_ENCODING=utf-8 +[ -z ${DEBUG_MODE+x} ] && export DEBUG_MODE=off + +# ZODB ENV Vars +[ -z ${ZODB_CACHE_SIZE+x} ] && export ZODB_CACHE_SIZE=50000 + +if [[ -v RELSTORAGE_DSN ]]; then + MSG="Using Relstorage configuration" + CONF=relstorage.conf + # Relstorage ENV Vars + [ -z ${RELSTORAGE_NAME+x} ] && export RELSTORAGE_NAME=storage + [ -z ${RELSTORAGE_READ_ONLY+x} ] && export RELSTORAGE_READ_ONLY=off + [ -z ${RELSTORAGE_KEEP_HISTORY+x} ] && export RELSTORAGE_KEEP_HISTORY=true + [ -z ${RELSTORAGE_COMMIT_LOCK_TIMEOUT+x} ] && export RELSTORAGE_COMMIT_LOCK_TIMEOUT=30 + [ -z ${RELSTORAGE_CREATE_SCHEMA+x} ] && export RELSTORAGE_CREATE_SCHEMA=true + [ -z ${RELSTORAGE_SHARED_BLOB_DIR+x} ] && export RELSTORAGE_SHARED_BLOB_DIR=false + [ -z ${RELSTORAGE_BLOB_CACHE_SIZE+x} ] && export RELSTORAGE_BLOB_CACHE_SIZE=100mb + [ -z ${RELSTORAGE_BLOB_CACHE_SIZE_CHECK+x} ] && export RELSTORAGE_BLOB_CACHE_SIZE_CHECK=10 + [ -z ${RELSTORAGE_BLOB_CACHE_SIZE_CHECK_EXTERNAL+x} ] && export RELSTORAGE_BLOB_CACHE_SIZE_CHECK_EXTERNAL=false + [ -z ${RELSTORAGE_BLOB_CHUNK_SIZE+x} ] && export RELSTORAGE_BLOB_CHUNK_SIZE=1048576 + [ -z ${RELSTORAGE_CACHE_LOCAL_MB+x} ] && export RELSTORAGE_CACHE_LOCAL_MB=10 + [ -z ${RELSTORAGE_CACHE_LOCAL_OBJECT_MAX+x} ] && export RELSTORAGE_CACHE_LOCAL_OBJECT_MAX=16384 + [ -z ${RELSTORAGE_CACHE_LOCAL_COMPRESSION+x} ] && export RELSTORAGE_CACHE_LOCAL_COMPRESSION=none + [ -z ${RELSTORAGE_CACHE_DELTA_SIZE_LIMIT+x} ] && export RELSTORAGE_CACHE_DELTA_SIZE_LIMIT=100000 +elif [[ -v ZEO_ADDRESS ]]; then + MSG="Using ZEO configuration" + CONF=zeo.conf + # Check ZEO variables + [ -z ${ZEO_SHARED_BLOB_DIR+x} ] && export ZEO_SHARED_BLOB_DIR=off + [ -z ${ZEO_READ_ONLY+x} ] && export ZEO_READ_ONLY=false + [ -z ${ZEO_CLIENT_READ_ONLY_FALLBACK+x} ] && export ZEO_CLIENT_READ_ONLY_FALLBACK=false + [ -z ${ZEO_STORAGE+x} ] && export ZEO_STORAGE=1 + [ -z ${ZEO_CLIENT_CACHE_SIZE+x} ] && export ZEO_CLIENT_CACHE_SIZE=128MB + [ -z ${ZEO_DROP_CACHE_RATHER_VERIFY+x} ] && export ZEO_DROP_CACHE_RATHER_VERIFY=false +else + MSG="Using default configuration" + CONF=zope.conf +fi + +# Handle ADDONS installation +if [[ -v ADDONS ]]; then + echo "=======================================================================================" + echo "Installing ADDONS ${ADDONS}" + echo "THIS IS NOT MEANT TO BE USED IN PRODUCTION" + echo "Read about it: https://github.com/plone/plone-backend/#extending-from-this-image" + echo "=======================================================================================" + /app/bin/pip install ${ADDONS} ${PIP_PARAMS} +fi + +# Handle development addons +if [[ -v DEVELOP ]]; then + echo "=======================================================================================" + echo "Installing DEVELOPment addons ${DEVELOP}" + echo "THIS IS NOT MEANT TO BE USED IN PRODUCTION" + echo "Read about it: https://github.com/plone/plone-backend/#extending-from-this-image" + echo "=======================================================================================" + /app/bin/pip install --editable ${DEVELOP} ${PIP_PARAMS} +fi + +# Handle Site creation +if [[ -v SITE ]]; then + export TYPE=${TYPE:-volto} + echo "=======================================================================================" + echo "Creating Plone ${TYPE} SITE: ${SITE}" + echo "Aditional profiles: ${PROFILES}" + echo "THIS IS NOT MEANT TO BE USED IN PRODUCTION" + echo "Read about it: https://github.com/plone/plone-backend/#extending-from-this-image" + echo "=======================================================================================" + export SITE_ID=${SITE} + $sudo /app/bin/zconsole run etc/${CONF} /app/scripts/create_site.py +fi + +if [[ "$1" == "start" ]]; then + echo $MSG + exec $sudo /app/bin/runwsgi -v etc/zope.ini config_file=${CONF} +elif [[ "$1" == "create-classic" ]]; then + export TYPE=classic + exec $sudo /app/bin/zconsole run etc/${CONF} /app/scripts/create_site.py +elif [[ "$1" == "create-volto" ]]; then + export TYPE=volto + exec $sudo /app/bin/zconsole run etc/${CONF} /app/scripts/create_site.py +elif [[ "$1" == "create-site" ]]; then + export TYPE=volto + exec $sudo /app/bin/zconsole run etc/${CONF} /app/scripts/create_site.py +else + # Custom + exec "$@" +fi diff --git a/6.0/6.0.0a3/skeleton/etc/relstorage.conf b/6.0/6.0.0a3/skeleton/etc/relstorage.conf new file mode 100644 index 0000000..da07f1f --- /dev/null +++ b/6.0/6.0.0a3/skeleton/etc/relstorage.conf @@ -0,0 +1,38 @@ +%define INSTANCE /app +instancehome $INSTANCE + +%define CLIENTHOME $(CLIENT_HOME) +clienthome $CLIENTHOME + +debug-mode $(DEBUG_MODE) +security-policy-implementation $(SECURITY_POLICY_IMPLEMENTATION) +verbose-security $(VERBOSE_SECURITY) +default-zpublisher-encoding $(DEFAULT_ZPUBLISHER_ENCODING) + + + zope_i18n_compile_mo_files true + CHAMELEON_CACHE $INSTANCE/var/cache + + + + # Main database + cache-size $(ZODB_CACHE_SIZE) + %import relstorage + + name $(RELSTORAGE_NAME) + read-only $(RELSTORAGE_READ_ONLY) + keep-history $(RELSTORAGE_KEEP_HISTORY) + commit-lock-timeout $(RELSTORAGE_COMMIT_LOCK_TIMEOUT) + create-schema $(RELSTORAGE_CREATE_SCHEMA) + blob-dir $INSTANCE/var/blobstorage + shared-blob-dir $(RELSTORAGE_SHARED_BLOB_DIR) + blob-cache-size $(RELSTORAGE_BLOB_CACHE_SIZE) + blob-cache-size-check $(RELSTORAGE_BLOB_CACHE_SIZE_CHECK) + blob-cache-size-check-external $(RELSTORAGE_BLOB_CACHE_SIZE_CHECK_EXTERNAL) + blob-chunk-size $(RELSTORAGE_BLOB_CHUNK_SIZE) + + dsn $(RELSTORAGE_DSN) + + + mount-point / + diff --git a/6.0/6.0.0a3/skeleton/etc/site.zcml b/6.0/6.0.0a3/skeleton/etc/site.zcml new file mode 100644 index 0000000..0eeb830 --- /dev/null +++ b/6.0/6.0.0a3/skeleton/etc/site.zcml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/6.0/6.0.0a3/skeleton/etc/zeo.conf b/6.0/6.0.0a3/skeleton/etc/zeo.conf new file mode 100644 index 0000000..1346e68 --- /dev/null +++ b/6.0/6.0.0a3/skeleton/etc/zeo.conf @@ -0,0 +1,33 @@ +%define INSTANCE /app +instancehome $INSTANCE + +%define CLIENTHOME $(CLIENT_HOME) +clienthome $CLIENTHOME + +debug-mode $(DEBUG_MODE) +security-policy-implementation $(SECURITY_POLICY_IMPLEMENTATION) +verbose-security $(VERBOSE_SECURITY) +default-zpublisher-encoding $(DEFAULT_ZPUBLISHER_ENCODING) + + + zope_i18n_compile_mo_files true + CHAMELEON_CACHE $INSTANCE/var/cache + + + + # Main database + cache-size $(ZODB_CACHE_SIZE) + + name zeostorage + var $INSTANCE/var + blob-dir $INSTANCE/var/blobstorage + read-only $(ZEO_READ_ONLY) + read-only-fallback $(ZEO_CLIENT_READ_ONLY_FALLBACK) + shared-blob-dir $(ZEO_SHARED_BLOB_DIR) + server $(ZEO_ADDRESS) + storage $(ZEO_STORAGE) + cache-size $(ZEO_CLIENT_CACHE_SIZE) + drop-cache-rather-verify $(ZEO_DROP_CACHE_RATHER_VERIFY) + + mount-point / + diff --git a/6.0/6.0.0a3/skeleton/etc/zope.conf b/6.0/6.0.0a3/skeleton/etc/zope.conf new file mode 100644 index 0000000..b0ed95e --- /dev/null +++ b/6.0/6.0.0a3/skeleton/etc/zope.conf @@ -0,0 +1,26 @@ +%define INSTANCE /app +instancehome $INSTANCE + +debug-mode $(DEBUG_MODE) +security-policy-implementation $(SECURITY_POLICY_IMPLEMENTATION) +verbose-security $(VERBOSE_SECURITY) +default-zpublisher-encoding $(DEFAULT_ZPUBLISHER_ENCODING) + + + zope_i18n_compile_mo_files true + CHAMELEON_CACHE $INSTANCE/var/cache + + + + # Main database + cache-size $(ZODB_CACHE_SIZE) + # Blob-enabled FileStorage database + + blob-dir $INSTANCE/var/blobstorage + # FileStorage database + + path $INSTANCE/var/filestorage/Data.fs + + + mount-point / + diff --git a/6.0/6.0.0a3/skeleton/etc/zope.ini b/6.0/6.0.0a3/skeleton/etc/zope.ini new file mode 100644 index 0000000..8ef8eaf --- /dev/null +++ b/6.0/6.0.0a3/skeleton/etc/zope.ini @@ -0,0 +1,77 @@ +[app:zope] +use = egg:Zope#main +zope_conf = %(here)s/%(config_file)s + +[server:main] +use = egg:waitress#main +host = 0.0.0.0 +port = 8080 +threads = 4 +clear_untrusted_proxy_headers = false +max_request_body_size = 1073741824 + + +[filter:translogger] +use = egg:Paste#translogger +setup_console_handler = False + +[pipeline:main] +pipeline = + egg:Zope#httpexceptions + translogger + zope + +[loggers] +keys = root, waitress.queue, waitress, wsgi + +[handlers] +keys = console, accesslog, eventlog + +[formatters] +keys = generic, message + +[formatter_generic] +format = %(asctime)s %(levelname)s [%(name)s:%(lineno)s][%(threadName)s] %(message)s +datefmt = %Y-%m-%d %H:%M:%S + +[formatter_message] +format = %(message)s + +[logger_root] +level = INFO +handlers = console, eventlog + +[logger_waitress.queue] +level = INFO +handlers = eventlog +qualname = waitress.queue +propagate = 0 + +[logger_waitress] +level = INFO +handlers = eventlog +qualname = waitress + +[logger_wsgi] +level = WARN +handlers = accesslog +qualname = wsgi +propagate = 0 + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[handler_accesslog] +class = StreamHandler +args = (sys.stdout,) +level = INFO +formatter = message + +[handler_eventlog] +class = StreamHandler +args = (sys.stderr,) +level = INFO +formatter = generic diff --git a/6.0/6.0.0a3/skeleton/inituser b/6.0/6.0.0a3/skeleton/inituser new file mode 100644 index 0000000..bb09481 --- /dev/null +++ b/6.0/6.0.0a3/skeleton/inituser @@ -0,0 +1 @@ +admin:{SHA}0DPiKuNIrrVmD8IUCuw1hQxNqZc= \ No newline at end of file diff --git a/6.0/6.0.0a3/skeleton/scripts/create_site.py b/6.0/6.0.0a3/skeleton/scripts/create_site.py new file mode 100644 index 0000000..d530d26 --- /dev/null +++ b/6.0/6.0.0a3/skeleton/scripts/create_site.py @@ -0,0 +1,86 @@ +from AccessControl.SecurityManagement import newSecurityManager +from Products.CMFPlone.factory import _DEFAULT_PROFILE +from Products.CMFPlone.factory import addPloneSite +from Testing.makerequest import makerequest + +import transaction +import os + + +truthy = frozenset(('t', 'true', 'y', 'yes', 'on', '1')) + + +def asbool(s): + """Return the boolean value ``True`` if the case-lowered value of string + input ``s`` is a :term:`truthy string`. If ``s`` is already one of the + boolean values ``True`` or ``False``, return it.""" + if s is None: + return False + if isinstance(s, bool): + return s + s = str(s).strip() + return s.lower() in truthy + + +app = makerequest(app) + +request = app.REQUEST + +admin = app.acl_users.getUserById("admin") +admin = admin.__of__(app.acl_users) +newSecurityManager(None, admin) + +# VARS +TYPE = os.getenv("TYPE", "volto") +SITE_ID = os.getenv("SITE_ID", "Plone") +SETUP_CONTENT = asbool(os.getenv("SETUP_CONTENT")) +DELETE_EXISTING = asbool(os.getenv("DELETE_EXISTING")) +LANGUAGE = os.getenv("LANGUAGE", "en") +TIMEZONE = os.getenv("TIMEZONE", "Europe/Berlin") +ADDITIONAL_PROFILES = os.getenv("PROFILES", os.getenv("ADDITIONAL_PROFILES", "")) + +PROFILES = { + "volto": [ + "plone.app.caching:default", + "plonetheme.barceloneta:default", + "plone.volto:default", + "plone.volto:default-homepage", + ], + "classic": [ + "plone.app.caching:default", + "plonetheme.barceloneta:default", + ], +} + + +def profile_ids(site_type): + extension_ids = PROFILES[site_type] + if ADDITIONAL_PROFILES: + extension_ids.extend( + [ + profile.strip() + for profile in ADDITIONAL_PROFILES.split(" ") + if profile.strip() + ] + ) + return extension_ids + + +payload = { + "title": "Plone", + "profile_id": _DEFAULT_PROFILE, + "extension_ids": profile_ids(TYPE), + "setup_content": SETUP_CONTENT, + "default_language": LANGUAGE, + "portal_timezone": TIMEZONE, +} + +if SITE_ID in app.objectIds() and DELETE_EXISTING: + app.manage_delObjects([SITE_ID]) + transaction.commit() + app._p_jar.sync() + +if SITE_ID not in app.objectIds(): + site = addPloneSite(app, SITE_ID, **payload) + transaction.commit() + app._p_jar.sync()