Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

volume name, permission to root group #8

Merged
merged 23 commits into from
Feb 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
4892e27
volume name, permission to root group
gpongelli Feb 1, 2023
95aa563
update active version package
gpongelli Feb 1, 2023
52db52e
comment
gpongelli Feb 1, 2023
386403b
dockerfile with correct name, avoid update json and readme.md on fail…
gpongelli Feb 1, 2023
cc74e37
docker api does not support multiple platform
gpongelli Feb 1, 2023
b89521f
Changed data folder name
gpongelli Feb 2, 2023
e2134e0
fix: filter success builds
gpongelli Feb 3, 2023
fae875d
fix links on readme
gpongelli Feb 3, 2023
187322e
feat: install gosu tool, not present on alpine 3.16
gpongelli Feb 3, 2023
7429631
entrypoint script that fixes user groups, folder, UID and GID
gpongelli Feb 3, 2023
5333c57
add new group back to avoid 'nogroup'.
gpongelli Feb 3, 2023
785176b
fix: push on checkout-ed branch
gpongelli Feb 3, 2023
ddcd37b
Entrypoint script on root
gpongelli Feb 4, 2023
fef98c6
Simplified entrypoint
gpongelli Feb 4, 2023
be3c46c
[ci skip] Cron execution on 2023-2-4
gpongelli Feb 4, 2023
2f0e139
revert json to start build
gpongelli Feb 4, 2023
0a887a3
fix: tty group for pgadmin4, gosu installation from official repo, ru…
gpongelli Feb 4, 2023
ae1c4d3
var lib pgadmin folder is the one that remains after container re-cre…
gpongelli Feb 4, 2023
002dae5
[ci skip] Cron execution on 2023-2-4
gpongelli Feb 4, 2023
52f9841
updated dockerhub link
gpongelli Feb 4, 2023
8da8b25
entrypoint called from root
gpongelli Feb 6, 2023
1cc2d43
empty file to start build
gpongelli Feb 6, 2023
eddbe35
[ci skip] Cron execution on 2023-2-6
gpongelli Feb 6, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/build_rpi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jobs:

outputs:
cmt_msg: ""
push_cmd: ""

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
Expand Down Expand Up @@ -95,6 +96,7 @@ jobs:
with:
script: |
core.setOutput('cmt_msg', '[ci skip] Cron execution on ${{ steps.get_date.outputs.year }}-${{ steps.get_date.outputs.month }}-${{ steps.get_date.outputs.day }}');
core.setOutput('push_cmd', 'origin ${{ github.ref_name }} --force');

- name: add new version to git
uses: EndBug/add-and-commit@v9.1.1
Expand All @@ -103,4 +105,4 @@ jobs:
author_name: ${{ secrets.GH_NAME }}
author_email: ${{ secrets.GH_EMAIL }}
message: ${{ steps.set_cmt_message.outputs.cmt_msg }}
push: 'origin main --force'
push: ${{ steps.set_cmt_message.outputs.push_cmd }}
89 changes: 81 additions & 8 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ ENV PGADMIN_VERSION=6.19
ENV PYTHONDONTWRITEBYTECODE=1
ENV CRYPTOGRAPHY_DONT_BUILD_RUST=1

# use piwheels to avoid building wheels
RUN echo "[global]" > /etc/pip.conf \
&& echo "extra-index-url=https://www.piwheels.org/simple" >> /etc/pip.conf

# install packages to compile pillow and the other python packages on ARM
RUN apk add --no-cache \
fribidi-dev \
Expand Down Expand Up @@ -42,19 +46,65 @@ RUN apk add --no-cache alpine-sdk linux-headers \
## -------------------------------------------
##

FROM python:3.11.1-alpine3.16 AS gosu_builder
MAINTAINER Gabriele Pongelli <gabriele.pongelli@gmail.com>

# switch to root, let the entrypoint drop back to pgadmin4
USER root

ENV GOSU_VERSION 1.16
RUN set -eux; \
\
apk add --no-cache --virtual .gosu-deps \
ca-certificates \
dpkg \
dirmngr \
gnupg \
; \
\
dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \
wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \
\
# verify the signature
export GNUPGHOME="$(mktemp -d)"; \
gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \
gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \
command -v gpgconf && gpgconf --kill all || :; \
rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \
\
# clean up fetch dependencies
apk del --no-network .gosu-deps; \
\
chmod +x /usr/local/bin/gosu; \
# verify that the binary works
gosu --version; \
gosu nobody true


##
## -------------------------------------------
##

FROM python:3.11.1-alpine3.16 AS app
MAINTAINER Gabriele Pongelli <gabriele.pongelli@gmail.com>

# switch to root, let the entrypoint drop back to pgadmin4
USER root

# create a non-privileged user to use at runtime, install non-devel packages
RUN addgroup -g 50 -S pgadmin \
&& adduser -D -S -h /pgadmin -s /sbin/nologin -u 1000 -G pgadmin pgadmin \
&& mkdir -p /pgadmin/config /pgadmin/storage \
&& chown -R 1000:50 /pgadmin \
RUN addgroup -g 50 -S pgadmin4 \
&& adduser -D -S -h /pgadmin4 -s /sbin/nologin -u 1000 -G pgadmin4 pgadmin4 \
&& mkdir -p /pgadmin4/config /pgadmin4/storage \
&& chown -R pgadmin4:pgadmin4 /pgadmin4 \
&& chmod -R g+rwX /pgadmin4 \
&& addgroup pgadmin4 tty \
&& apk add \
fribidi \
freetype \
harfbuzz \
lcms2 \
libc6-compat \
libedit \
libffi \
libimagequant \
Expand All @@ -71,13 +121,36 @@ RUN addgroup -g 50 -S pgadmin \
tk \
zlib


EXPOSE 5050

# add an entry-point script
ENV ENTRY_POINT="entrypoint.sh"
COPY ${ENTRY_POINT} /${ENTRY_POINT}
RUN chmod 755 /${ENTRY_POINT}
ENV ENTRY_POINT=

# copy from builder image
COPY --from=builder /usr/local/lib/python3.11/ /usr/local/lib/python3.11/

# copy gosu from other image
COPY --from=gosu_builder /usr/local/bin/gosu /usr/local/bin/gosu
RUN chmod +x /usr/local/bin/gosu;

# copy from local folder
COPY LICENSE config_distro.py /usr/local/lib/python3.11/site-packages/pgadmin4/

USER pgadmin:pgadmin
CMD python /usr/local/lib/python3.11/site-packages/pgadmin4/pgAdmin4.py
VOLUME /pgadmin/
WORKDIR /pgadmin4
VOLUME /pgadmin4

# run entrypoint as root, gosu will change
#USER pgadmin4

# using exec form to run also CMD into the entrypoint.
# shell form will ignore CMD or docker run command line arguments
# ref https://docs.docker.com/engine/reference/builder/#shell-form-entrypoint-example
ENTRYPOINT ["/entrypoint.sh"]

# https://docs.docker.com/engine/reference/builder/#understand-how-cmd-and-entrypoint-interact
# the exec form works correctly when launching with user: 0 and without it
# the shell form does starting loop if launched as pgadmin4 user
CMD ["python", "/usr/local/lib/python3.11/site-packages/pgadmin4/pgAdmin4.py"]
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
[![Pulls](https://img.shields.io/docker/pulls/gpongelli/pgadmin4-docker-armv7.svg?style=flat-square&logo=docker)](https://hub.docker.com/r/gpongelli/pgadmin4-docker-armv7/)
[![DockerBuild](https://img.shields.io/docker/cloud/build/gpongelli/pgadmin4-docker-armv7.svg?style=flat-square&logo=docker)](https://hub.docker.com/r/gpongelli/pgadmin4-docker-armv7/)
[![Pulls](https://img.shields.io/docker/pulls/gpongelli/pgadmin4-arm.svg?style=flat-square&logo=docker)](https://hub.docker.com/r/gpongelli/pgadmin4-arm/)


Last updated by bot: 2023-01-19
Expand Down Expand Up @@ -34,9 +33,10 @@ Starting from that project, I've added the github actions to build docker image
## Tags
To use a specific combination of pgAdmin 4 and python see the following table of available image tags.

| Tag | pgAdmin version | Python version | Distro | Architecture |
|-----------------|-------------------|------------------|------------|----------------|
| `6.19-py3.11` | 6.19 | 3.11.1 | alpine3.16 | armv7 |
| Tag | pgAdmin version | Python version | Distro | Architecture |
|-----------------------|-------------------|------------------|------------|----------------|
| `6.19-py3.11-armv7` | 6.19 | 3.11.1 | alpine3.16 | armv7 |
| `6.19-py3.11-armv8` | 6.19 | 3.11.1 | alpine3.16 | armv8 |

Lovely! These tags are kept updated automatically (when new minor or patch version are released) by `build_versions.py` which is run twice a day on CircleCI.

Expand Down
57 changes: 34 additions & 23 deletions build_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
DISTRO_TEMPLATE = {'alpine3.16': 'raspberry'}
ARCHS = {'armv7': 'linux/arm/v7', 'armv8': 'linux/arm64'}

# same keys of ARCHS used to download gosu tool
# https://github.com/tianon/gosu/releases
GOSU_ARCH = {'armv7': 'armhf', 'armv8': 'arm64'}
GOSU_VERSION = "1.16"


todays_date = datetime.utcnow().date().isoformat()

Expand Down Expand Up @@ -141,22 +146,25 @@ def version_combinations(pgadmin_versions, python_versions):
for pg in pgadmin_versions:
if pg["release_date"] < p["start_date"] or pg["release_date"] > p["end_date"]:
continue
distro = f'-{p["distro"]}' if p["distro"] != DEFAULT_DISTRO else ""
key = f'{pg["version"]}-py{p["key"]}{distro}'
versions.append(
{
"key": key,
"python": p["key"],
"python_canonical": p["canonical_version"],
"python_image": p["image"],
"pgadmin": pg["version"],
"pgadmin_canonical": pg["version"]+".0",
"pgadmin_whl": pg["file_whl"],
"distro": p["distro"],
"arch": ','.join(ARCHS.keys()),
"docker_arch": ','.join(ARCHS.values()),
}
)
for _a, _d in ARCHS.items():
distro = f'-{p["distro"]}' if p["distro"] != DEFAULT_DISTRO else ""
key = f'{pg["version"]}-py{p["key"]}{distro}-{_a}'
versions.append(
{
"key": key,
"python": p["key"],
"python_canonical": p["canonical_version"],
"python_image": p["image"],
"pgadmin": pg["version"],
"pgadmin_canonical": pg["version"]+".0",
"pgadmin_whl": pg["file_whl"],
"distro": p["distro"],
"arch": _a,
"docker_arch": _d,
"gosu_arch": GOSU_ARCH[_a],
"gosu_version": GOSU_VERSION,
}
)

versions = sorted(versions, key=lambda v: DISTROS.index(v["distro"]))
versions = sorted(versions, key=lambda v: by_semver_key(v["python_canonical"]), reverse=True)
Expand Down Expand Up @@ -208,7 +216,7 @@ def get_new_versions(current_versions, versions):

def build_new_or_updated(new_or_updated, dry_run=False, debug=False):

# Login to docker hub
# Login to docker hub, docker client app on host MUST be running
docker_client = docker.from_env()
dockerhub_username = os.getenv("DOCKERHUB_USERNAME")
try:
Expand All @@ -222,7 +230,7 @@ def build_new_or_updated(new_or_updated, dry_run=False, debug=False):
failed_builds = []

def build_image(version):
_file_suffix = f"{version['key']}-{str(version['arch']).replace(',' ,'_')}"
_file_suffix = f"{version['key']}-{version['arch']}"
dockerfile = render_dockerfile(version)
# docker build wants bytes
with BytesIO(dockerfile.encode()) as fileobj:
Expand All @@ -240,7 +248,7 @@ def build_image(version):
try:
if not dry_run:
docker_client.images.build(path=os.getcwd(),
dockerfile="tmp.Dockerfile",
dockerfile=f"tmp.Dockerfile-{_file_suffix}",
tag=tag,
rm=True,
platform=version['docker_arch'],
Expand All @@ -263,6 +271,7 @@ def build_image(version):
logging.warning("Retrying... %s retries left", retries)
except docker.errors.BuildError as e:
logging.error("Failed building %s, skipping...", version)
logging.error(e)
failed_builds.append(version)

with ThreadPoolExecutor(max_workers=4) as executor:
Expand Down Expand Up @@ -348,11 +357,13 @@ def main(distros, dry_run, debug, pgadmin_min_ver, python_min_ver):
if new_versions := get_new_versions(current_versions, versions):
# Build tag and release docker images
failed_builds = build_new_or_updated(new_versions, dry_run, debug)
success_builds = [v for v in versions if v not in failed_builds]

# persist image data after build ended
persist_versions(versions, dry_run)
update_readme_tags_table(versions, dry_run)
save_latest_dockerfile(pgadmin_versions, python_min_ver)
if success_builds:
# persist image data after build ended
persist_versions(success_builds, dry_run)
update_readme_tags_table(success_builds, dry_run)
save_latest_dockerfile(pgadmin_versions, python_min_ver)

# FIXME(perf): Generate a CircleCI config file with a workflow (parallell) and trigger this workflow via the API.
# Ref: https://circleci.com/docs/2.0/api-job-trigger/
Expand Down
2 changes: 1 addition & 1 deletion config_distro.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

# Data directory for storage of config settings etc. This shouldn't normally
# need to be changed - it's here as various other settings depend on it.
DATA_DIR = os.getenv('PG_ADMIN_DATA_DIR', '/pgadmin/')
DATA_DIR = os.getenv('PG_ADMIN_DATA_DIR', '/pgadmin4/')


##########################################################################
Expand Down
31 changes: 21 additions & 10 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
#!/bin/bash
export HOME=/pgadmin
if ! whoami &> /dev/null; then
if [ -w /etc/passwd ]; then
# Required for running in OpenShift
echo "${USER_NAME:-pga}:x:$(id -u):0:${USER_NAME:-pga} user:${HOME}:/bin/bash" >> /etc/passwd
fi
fi

exec $@
#!/bin/ash
set -e -x

export HOME=/pgadmin4

# ref https://docs.docker.com/engine/reference/builder/#entrypoint
#trap "echo TRAPed signal" HUP INT QUIT TERM


# can be overridden in the service definition, 1000 and 50 comes from Dockerfile
PUID=${PUID:-1000}
PGID=${PGID:-50}

chown -R "$PUID:$PGID" "$HOME"

# beign able to write to /dev/stdout not only by root user
# https://github.com/moby/moby/issues/31243
chmod o+w /dev/stdout

# Add call to gosu to drop from root user to pgadmin4 user
exec gosu pgadmin4 "$@"
Loading