Created Docker image for 2.5.0

This commit is contained in:
Ramon Bartl 2024-01-03 18:06:46 +01:00
parent b40605ade8
commit c8a1e3c834
No known key found for this signature in database
GPG key ID: 359669D35BDDF79B
11 changed files with 535 additions and 4 deletions

67
2.5.0/Dockerfile Normal file
View file

@ -0,0 +1,67 @@
# Use an official Python runtime as a parent image
FROM python:2.7-slim-buster
# Set one or more individual labels
LABEL maintainer="Ramon Bartl"
LABEL email="rb@ridingbytes.com"
LABEL senaite.core.version="v2.5.0"
# Set environment variables
ENV PLONE_MAJOR=5.2 \
PLONE_VERSION=5.2.14 \
PLONE_MD5=e8e1f774f069026319be3038631e0734 \
PLONE_UNIFIED_INSTALLER=Plone-5.2.14-UnifiedInstaller-1.0 \
SENAITE_HOME=/home/senaite \
SENAITE_USER=senaite \
SENAITE_INSTANCE_HOME=/home/senaite/senaitelims \
SENAITE_DATA=/data \
SENAITE_FILESTORAGE=/data/filestorage \
SENAITE_BLOBSTORAGE=/data/blobstorage
# Create the senaite user
RUN useradd --system -m -d $SENAITE_HOME -U -u 500 $SENAITE_USER
# Create directories
RUN mkdir -p $SENAITE_INSTANCE_HOME $SENAITE_FILESTORAGE $SENAITE_BLOBSTORAGE
# Copy Buildout
COPY requirements.txt buildout.cfg $SENAITE_INSTANCE_HOME/
# Copy the build dependencies and startup scripts
COPY build_deps.txt run_deps.txt docker-initialize.py docker-entrypoint.sh /
# Note: we concatenate all commands to avoid multiple layer generation and reduce the image size
RUN apt-get update \
# Install system pakages
&& apt-get install -y --no-install-recommends $(grep -vE "^\s*#" /build_deps.txt | tr "\n" " ") \
&& apt-get install -y --no-install-recommends $(grep -vE "^\s*#" /run_deps.txt | tr "\n" " ") \
# Fetch unified installer
&& wget -O Plone.tgz https://launchpad.net/plone/$PLONE_MAJOR/$PLONE_VERSION/+download/$PLONE_UNIFIED_INSTALLER.tgz \
&& echo "$PLONE_MD5 Plone.tgz" | md5sum -c - \
&& tar -xzf /Plone.tgz \
&& cp -rv $PLONE_UNIFIED_INSTALLER/base_skeleton/* $SENAITE_INSTANCE_HOME \
&& cp -v $PLONE_UNIFIED_INSTALLER/buildout_templates/buildout.cfg $SENAITE_INSTANCE_HOME/buildout-base.cfg \
&& rm -rf $PLONE_UNIFIED_INSTALLER Plone.tgz \
# Buildout
&& cd $SENAITE_INSTANCE_HOME \
&& pip install -r requirements.txt \
&& buildout \
&& ln -s $SENAITE_FILESTORAGE/ var/filestorage \
&& ln -s $SENAITE_BLOBSTORAGE/ var/blobstorage \
&& chown -R senaite:senaite $SENAITE_HOME $SENAITE_DATA \
# Cleanup
&& apt-get purge -y --auto-remove $(grep -vE "^\s*#" /build_deps.txt | tr "\n" " ") \
&& rm -rf /$SENAITE_HOME/buildout-cache \
&& rm -rf /var/lib/apt/lists/*
# Change working directory
WORKDIR $SENAITE_INSTANCE_HOME
# Mount external volume
VOLUME /data
# Expose instance port
EXPOSE 8080
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["start"]

14
2.5.0/build_deps.txt Normal file
View file

@ -0,0 +1,14 @@
dpkg-dev
gcc
libbz2-dev
libc6-dev
libffi-dev
libjpeg62-turbo-dev
libopenjp2-7-dev
libpcre3-dev
libssl-dev
libtiff5-dev
libxml2-dev
libxslt1-dev
wget
zlib1g-dev

57
2.5.0/buildout.cfg Normal file
View file

@ -0,0 +1,57 @@
[buildout]
index = https://pypi.python.org/simple/
extends =
buildout-base.cfg
extensions = mr.developer
var-dir=/data
user=admin:admin
effective-user = senaite
buildout-user = senaite
eggs-directory=eggs
download-cache=../buildout-cache/downloads
parts +=
zeo
plonesite
console_scripts
eggs +=
senaite.lims
[client1]
recipe =
[zeo]
<= zeoserver_base
recipe = plone.recipe.zeoserver
zeo-address = 8080
[instance]
# taken from https://github.com/plone/plone.docker/blob/master/5.2/5.2.5/python2/buildout.cfg
event-log-handler = StreamHandler
event-log-args = (sys.stderr,)
access-log-handler = StreamHandler
access-log-args = (sys.stdout,)
[console_scripts]
recipe = zc.recipe.egg:scripts
eggs = senaite.core
[plonesite]
recipe = collective.recipe.plonesite
instance = instance
site-id = senaite
profiles-initial = Products.CMFPlone:dependencies
profiles =
senaite.lims:default
upgrade-portal = False
upgrade-all-profiles = False
enabled = False
[versions]
setuptools =
zc.buildout =
senaite.lims = 2.5.0

41
2.5.0/docker-compose.yml Normal file
View file

@ -0,0 +1,41 @@
version: "3"
services:
zeo:
image: senaite/senaite:v2.5.0
command: zeo
volumes:
- ./data:/data
instance1:
image: senaite/senaite:v2.5.0
ports:
- 8081:8080
links:
- zeo
deploy:
resources:
limits:
cpus: "1"
memory: 2048MB
environment:
ZEO_ADDRESS: "zeo:8080"
instance2:
image: senaite/senaite:v2.5.0
ports:
- 8082:8080
links:
- zeo
deploy:
resources:
limits:
cpus: "1"
memory: 2048MB
environment:
ZEO_ADDRESS: "zeo:8080"
volumes:
data:
external: true

55
2.5.0/docker-entrypoint.sh Executable file
View file

@ -0,0 +1,55 @@
#!/bin/bash
set -e
COMMANDS="adduser debug fg foreground help kill logreopen logtail reopen_transcript run show status stop wait"
START="console start restart"
# Fixing permissions for external /data volumes
mkdir -p /data/blobstorage /data/cache /data/filestorage /data/instance /data/log /data/zeoserver
mkdir -p /home/senaite/senaitelims/src
find /data -not -user senaite -exec chown senaite:senaite {} \+
find /home/senaite -not -user senaite -exec chown senaite:senaite {} \+
# Initializing from environment variables
gosu senaite python /docker-initialize.py
function git_fixture {
for d in `find /home/senaite/senaitelims/src -mindepth 1 -maxdepth 1 -type d`
do
if [ -d "$d/.git" ]; then
git config --global --add safe.directory $d
echo "git config --global --add safe.directory $d"
fi
done
}
# Fix mr.developer: fatal: detected dubious ownership in repository at ...
# https://github.com/actions/runner-images/issues/6775
# https://github.com/senaite/senaite.docker/issues/17
git_fixture
if [ -e "custom.cfg" ]; then
buildout -c custom.cfg
find /data -not -user senaite -exec chown senaite:senaite {} \+
find /home/senaite -not -user senaite -exec chown senaite:senaite {} \+
gosu senaite python /docker-initialize.py
fi
# ZEO Server
if [[ "$1" == "zeo"* ]]; then
exec gosu senaite bin/$1 fg
fi
# Instance start
if [[ $START == *"$1"* ]]; then
exec gosu senaite bin/instance console
fi
# Instance helpers
if [[ $COMMANDS == *"$1"* ]]; then
exec gosu senaite bin/instance "$@"
fi
# Custom
exec "$@"

282
2.5.0/docker-initialize.py Executable file
View file

@ -0,0 +1,282 @@
#!/usr/local/bin/python
import re
import os
class Environment(object):
""" Configure container via environment variables
"""
def __init__(
self, env=os.environ,
zope_conf="/home/senaite/senaitelims/parts/instance/etc/zope.conf",
custom_conf="/home/senaite/senaitelims/custom.cfg",
zeopack_conf="/home/senaite/senaitelims/bin/zeopack",
zeoserver_conf="/home/senaite/senaitelims/parts/zeoserver/etc/zeo.conf",
cors_conf="/home/senaite/senaitelims/parts/instance/etc/package-includes/999-additional-overrides.zcml"
):
self.env = env
self.zope_conf = zope_conf
self.custom_conf = custom_conf
self.zeopack_conf = zeopack_conf
self.zeoserver_conf = zeoserver_conf
self.cors_conf = cors_conf
def zeoclient(self):
""" ZEO Client
"""
server = self.env.get("ZEO_ADDRESS", None)
if not server:
return
config = ""
with open(self.zope_conf, "r") as cfile:
config = cfile.read()
# Already initialized
if "<blobstorage>" not in config:
return
read_only = self.env.get("ZEO_READ_ONLY", "false")
zeo_ro_fallback = self.env.get("ZEO_CLIENT_READ_ONLY_FALLBACK", "false")
shared_blob_dir = self.env.get("ZEO_SHARED_BLOB_DIR", "off")
zeo_storage = self.env.get("ZEO_STORAGE", "1")
zeo_client_cache_size = self.env.get("ZEO_CLIENT_CACHE_SIZE", "128MB")
zeo_conf = ZEO_TEMPLATE.format(
zeo_address=server,
read_only=read_only,
zeo_client_read_only_fallback=zeo_ro_fallback,
shared_blob_dir=shared_blob_dir,
zeo_storage=zeo_storage,
zeo_client_cache_size=zeo_client_cache_size
)
pattern = re.compile(r"<blobstorage>.+</blobstorage>", re.DOTALL)
config = re.sub(pattern, zeo_conf, config)
with open(self.zope_conf, "w") as cfile:
cfile.write(config)
def zeopack(self):
""" ZEO Pack
"""
server = self.env.get("ZEO_ADDRESS", None)
if not server:
return
if ":" in server:
host, port = server.split(":")
else:
host, port = (server, "8100")
with open(self.zeopack_conf, 'r') as cfile:
text = cfile.read()
text = text.replace('address = "8100"', 'address = "%s"' % server)
text = text.replace('host = "127.0.0.1"', 'host = "%s"' % host)
text = text.replace('port = "8100"', 'port = "%s"' % port)
with open(self.zeopack_conf, 'w') as cfile:
cfile.write(text)
def zeoserver(self):
""" ZEO Server
"""
pack_keep_old = self.env.get("ZEO_PACK_KEEP_OLD", '')
if pack_keep_old.lower() in ("false", "no", "0", "n", "f"):
with open(self.zeoserver_conf, 'r') as cfile:
text = cfile.read()
if 'pack-keep-old' not in text:
text = text.replace(
'</filestorage>',
' pack-keep-old false\n</filestorage>'
)
with open(self.zeoserver_conf, 'w') as cfile:
cfile.write(text)
def cors(self):
""" Configure CORS Policies
"""
if not [e for e in self.env if e.startswith("CORS_")]:
return
allow_origin = self.env.get(
"CORS_ALLOW_ORIGIN",
"http://localhost:3000,http://127.0.0.1:3000")
allow_methods = self.env.get(
"CORS_ALLOW_METHODS",
"DELETE,GET,OPTIONS,PATCH,POST,PUT")
allow_credentials = self.env.get(
"CORS_ALLOW_CREDENTIALS",
"true")
expose_headers = self.env.get(
"CORS_EXPOSE_HEADERS",
"Content-Length,X-My-Header")
allow_headers = self.env.get(
"CORS_ALLOW_HEADERS",
"Accept,Authorization,Content-Type,X-Custom-Header")
max_age = self.env.get(
"CORS_MAX_AGE",
"3600")
cors_conf = CORS_TEMPLACE.format(
allow_origin=allow_origin,
allow_methods=allow_methods,
allow_credentials=allow_credentials,
expose_headers=expose_headers,
allow_headers=allow_headers,
max_age=max_age
)
with open(self.cors_conf, "w") as cfile:
cfile.write(cors_conf)
def buildout(self):
""" Buildout from environment variables
"""
# Already configured
if os.path.exists(self.custom_conf):
return
findlinks = self.env.get("FIND_LINKS", "").strip().split()
eggs = self.env.get("PLONE_ADDONS",
self.env.get("ADDONS", "")).strip().split()
zcml = self.env.get("PLONE_ZCML",
self.env.get("ZCML", "")).strip().split()
develop = self.env.get("PLONE_DEVELOP",
self.env.get("DEVELOP", "")).strip().split()
site = self.env.get("PLONE_SITE",
self.env.get("SITE", "")).strip()
profiles = self.env.get("PLONE_PROFILES",
self.env.get("PROFILES", "")).strip().split()
versions = self.env.get("PLONE_VERSIONS",
self.env.get("VERSIONS", "")).strip().split()
sources = self.env.get("SOURCES", "").strip().split(",")
password = self.env.get("PASSWORD", "").strip()
# If profiles not provided. Install ADDONS :default profiles
if not profiles:
for egg in eggs:
base = egg.split("=")[0]
profiles.append("%s:default" % base)
if not (eggs or zcml or develop or site or password):
return
buildout = BUILDOUT_TEMPLATE.format(
password=password or "admin",
findlinks="\n\t".join(findlinks),
eggs="\n\t".join(eggs),
zcml="\n\t".join(zcml),
develop="\n\t".join(develop),
versions="\n".join(versions),
sources="\n".join(sources),
)
if site:
buildout += PLONESITE_TEMPLATE.format(
site=site,
profiles="\n\t".join(profiles),
)
# If we need to create a senaitesite and we have a zeo setup
# configure collective.recipe.senaitesite properly
server = self.env.get("ZEO_ADDRESS", None)
if server:
buildout += ZEO_INSTANCE_TEMPLATE.format(
zeoaddress=server,
)
with open(self.custom_conf, 'w') as cfile:
cfile.write(buildout)
def setup(self, **kwargs):
self.buildout()
self.cors()
self.zeoclient()
self.zeopack()
self.zeoserver()
__call__ = setup
ZEO_TEMPLATE = """
<zeoclient>
read-only {read_only}
read-only-fallback {zeo_client_read_only_fallback}
blob-dir /data/blobstorage
shared-blob-dir {shared_blob_dir}
server {zeo_address}
storage {zeo_storage}
name zeostorage
var /home/senaite/senaitelims/parts/instance/var
cache-size {zeo_client_cache_size}
</zeoclient>
""".strip()
CORS_TEMPLACE = """<configure
xmlns="http://namespaces.zope.org/zope">
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:plone="http://namespaces.plone.org/plone">
<plone:CORSPolicy
allow_origin="{allow_origin}"
allow_methods="{allow_methods}"
allow_credentials="{allow_credentials}"
expose_headers="{expose_headers}"
allow_headers="{allow_headers}"
max_age="{max_age}"
/>
</configure>
</configure>
"""
BUILDOUT_TEMPLATE = """
[buildout]
extends = buildout.cfg
user=admin:{password}
find-links += {findlinks}
develop += {develop}
eggs += {eggs}
zcml += {zcml}
[versions]
{versions}
[sources]
{sources}
"""
PLONESITE_TEMPLATE = """
[plonesite]
enabled = true
site-id = {site}
profiles += {profiles}
"""
ZEO_INSTANCE_TEMPLATE = """
[instance]
zeo-client = true
zeo-address = {zeoaddress}
shared-blob = off
http-fast-listen = off
"""
def initialize():
""" Configure Instance as ZEO Client
"""
environment = Environment()
environment.setup()
if __name__ == "__main__":
initialize()

1
2.5.0/requirements.txt Normal file
View file

@ -0,0 +1 @@
-r https://dist.plone.org/release/5.2.14/requirements.txt

14
2.5.0/run_deps.txt Normal file
View file

@ -0,0 +1,14 @@
git
gosu
libcairo2
libgdk-pixbuf2.0-0
libjpeg62
libopenjp2-7
libpango-1.0-0
libpangocairo-1.0-0
libtiff5
libxml2
libxslt1.1
lynx
rsync
vim

View file

@ -9,8 +9,8 @@ LABEL senaite.core.version="latest"
# Set environment variables
ENV PLONE_MAJOR=5.2 \
PLONE_VERSION=5.2.13 \
PLONE_MD5=12c037fae9413385149e8677f8457b84 \
PLONE_UNIFIED_INSTALLER=Plone-5.2.13-UnifiedInstaller-1.0 \
PLONE_MD5=e8e1f774f069026319be3038631e0734 \
PLONE_UNIFIED_INSTALLER=Plone-5.2.14-UnifiedInstaller-1.0 \
SENAITE_HOME=/home/senaite \
SENAITE_USER=senaite \
SENAITE_INSTANCE_HOME=/home/senaite/senaitelims \

View file

@ -1 +1 @@
-r https://dist.plone.org/release/5.2.13/requirements.txt
-r https://dist.plone.org/release/5.2.14/requirements.txt

View file

@ -1,7 +1,7 @@
version: "3"
services:
instance:
image: senaite/senaite:v2.4.1
image: senaite/senaite:v2.5.0
restart: "unless-stopped"
ports:
- "8080:8080"