Skip to content

Commit 6da409c

Browse files
CI: Two Machines (#402)
This patch starts using two machines to test testgres. Key things - Option '--init' is used in a command docker run to prevent a problem with lots of zomby processes on altlinux-11, alpine-pg10 and alpine-pg11 targets. Co-authored-by: Mark G. mark@google.com
1 parent 946054e commit 6da409c

11 files changed

Lines changed: 691 additions & 487 deletions

.github/workflows/ci.yml

Lines changed: 138 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -74,82 +74,115 @@ jobs:
7474
fail-fast: false
7575
matrix:
7676
include:
77-
- platform: "std2-all"
77+
- platform: "alpine"
7878
python: "3.7"
7979
postgres: "17"
80-
- platform: "std2-all"
80+
case_suffix: "py3_07_xx-pg17_xx"
81+
- platform: "alpine"
8182
python: "3.8.0"
8283
postgres: "17"
83-
- platform: "std2-all"
84+
case_suffix: "py3_08_88-pg17_xx"
85+
- platform: "alpine"
8486
python: "3.8"
8587
postgres: "17"
86-
- platform: "std2-all"
88+
case_suffix: "py3_08_xx-pg17_xx"
89+
- platform: "alpine"
8790
python: "3.9"
8891
postgres: "17"
89-
- platform: "std2-all"
92+
case_suffix: "py3_09_xx-pg17_xx"
93+
- platform: "alpine"
9094
python: "3.10"
9195
postgres: "17"
92-
- platform: "std2-all"
96+
case_suffix: "py3_10_xx-pg17_xx"
97+
- platform: "alpine"
9398
python: "3.11"
9499
postgres: "17"
95-
- platform: "std2-all"
100+
case_suffix: "py3_11_xx-pg17_xx"
101+
- platform: "alpine"
96102
python: "3.12"
97103
postgres: "17"
98-
- platform: "std2-all"
104+
case_suffix: "py3_12_xx-pg17_xx"
105+
- platform: "alpine"
99106
python: "3.13"
100107
postgres: "17"
101-
- platform: "std2-all"
108+
case_suffix: "py3_13_xx-pg17_xx"
109+
- platform: "alpine"
102110
python: "3.14"
103111
postgres: "17"
104-
- platform: "std"
105-
python: "3"
112+
case_suffix: "py3_14_xx-pg17_xx"
113+
- platform: "alpine"
114+
python: "3.12"
106115
postgres: "10"
107-
- platform: "std"
108-
python: "3"
116+
case_suffix: "py3_12_xx-pg10_xx"
117+
- platform: "alpine"
118+
python: "3.12"
109119
postgres: "11"
110-
- platform: "std"
111-
python: "3"
120+
case_suffix: "py3_12_xx-pg11_xx"
121+
- platform: "alpine"
122+
python: "3.12"
112123
postgres: "12"
113-
- platform: "std"
114-
python: "3"
124+
case_suffix: "py3_12_xx-pg12_xx"
125+
- platform: "alpine"
126+
python: "3.12"
115127
postgres: "13"
116-
- platform: "std"
117-
python: "3"
128+
case_suffix: "py3_12_xx-pg13_xx"
129+
- platform: "alpine"
130+
python: "3.12"
118131
postgres: "14"
119-
- platform: "std"
120-
python: "3"
132+
case_suffix: "py3_12_xx-pg14_xx"
133+
- platform: "alpine"
134+
python: "3.12"
121135
postgres: "15"
122-
- platform: "std"
123-
python: "3"
136+
case_suffix: "py3_12_xx-pg15_xx"
137+
- platform: "alpine"
138+
python: "3.12"
124139
postgres: "16"
125-
- platform: "std-all"
126-
python: "3"
127-
postgres: "17"
128-
- platform: "std-all"
129-
python: "3"
140+
case_suffix: "py3_12_xx-pg16_xx"
141+
- platform: "alpine"
142+
python: "3.12"
130143
postgres: "18"
144+
case_suffix: "py3_12_xx-pg18_xx"
131145
- platform: "ubuntu_24_04"
132146
python: "3"
133147
postgres: "17"
148+
case_suffix: "py3_xx_xx-pg17_xx"
134149
- platform: "altlinux_10"
135150
python: "3"
136151
postgres: "17"
152+
case_suffix: "py3_xx_xx-pg17_xx"
137153
- platform: "altlinux_11"
138154
python: "3"
139155
postgres: "17"
156+
case_suffix: "py3_xx_xx-pg17_xx"
140157
- platform: "astralinux_1_7"
141158
python: "3"
142159
postgres: "17"
160+
case_suffix: "py3_xx_xx-pg17_xx"
161+
162+
name: "test: ${{ matrix.platform }} | ${{ matrix.case_suffix }}"
143163

144164
env:
145-
BASE_SIGN: "${{ matrix.platform }}-py${{ matrix.python }}-pg${{ matrix.postgres }}"
165+
BASE_SIGN: "${{ matrix.platform }}-${{ matrix.case_suffix }}"
166+
167+
DOCKER_HIGHLOAD_FLAGS: >-
168+
--init
169+
--sysctl net.core.somaxconn=4096
170+
--sysctl net.ipv4.tcp_max_syn_backlog=4096
171+
--ulimit nofile=524288:524288
172+
--ulimit nproc=65535:65535
173+
--sysctl net.ipv4.tcp_tw_reuse=1
174+
--sysctl net.ipv4.ip_local_port_range="1024 65535"
146175
147176
steps:
148177
- name: Prepare variables
149178
run: |
150179
echo "RUN_CFG__NOW=$(date +'%Y%m%d_%H%M%S')" >> $GITHUB_ENV
151180
echo "RUN_CFG__LOGS_DIR=logs-${{ env.BASE_SIGN }}" >> $GITHUB_ENV
152181
echo "RUN_CFG__DOCKER_IMAGE_NAME=tests-${{ env.BASE_SIGN }}" >> $GITHUB_ENV
182+
echo "RUN_CFG__NETWORK_NAME=network-${{ env.BASE_SIGN }}" >> $GITHUB_ENV
183+
echo "RUN_CFG__MACHINE1_NAME=machine1-${{ env.BASE_SIGN }}" >> $GITHUB_ENV
184+
echo "RUN_CFG__MACHINE2_NAME=machine2-${{ env.BASE_SIGN }}" >> $GITHUB_ENV
185+
echo "RUN_CFG__SSH_KEY_NAME=id_ed25519-${{ env.BASE_SIGN }}" >> $GITHUB_ENV
153186
echo "---------- [$GITHUB_ENV]"
154187
cat $GITHUB_ENV
155188
- name: Checkout
@@ -158,10 +191,83 @@ jobs:
158191
run: mkdir -p "${{ env.RUN_CFG__LOGS_DIR }}"
159192
- name: Adjust logs folder permission
160193
run: chmod -R 777 "${{ env.RUN_CFG__LOGS_DIR }}"
161-
- name: Build local image ${{ matrix.alpine }}
194+
- name: Build local image
162195
run: docker build --build-arg PG_VERSION="${{ matrix.postgres }}" --build-arg PYTHON_VERSION="${{ matrix.python }}" -t "${{ env.RUN_CFG__DOCKER_IMAGE_NAME }}" -f Dockerfile--${{ matrix.platform }}.tmpl .
163-
- name: Run
164-
run: docker run $(bash <(curl -s https://codecov.io/env)) -t -v ${{ github.workspace }}/${{ env.RUN_CFG__LOGS_DIR }}:/home/test/testgres/logs "${{ env.RUN_CFG__DOCKER_IMAGE_NAME }}"
196+
- name: Generate temporary SSH Key pair for CI
197+
run: |
198+
# Generate a key without a password directly on the GitHub Actions host
199+
ssh-keygen -q -t ed25519 -N "" -f ${{ github.workspace }}/${{ env.RUN_CFG__SSH_KEY_NAME }}
200+
201+
- name: Setup network
202+
run: |
203+
docker network create ${{ env.RUN_CFG__NETWORK_NAME }}
204+
205+
- name: Run machine2 (for remote operations)
206+
run: |
207+
#
208+
# "- sleep infinity" is a right command. Docker ignores "-" and runs "sleep infinity".
209+
#
210+
docker run -d -t \
211+
${{ env.DOCKER_HIGHLOAD_FLAGS }} \
212+
--sysctl net.ipv4.tcp_tw_reuse=1 \
213+
--sysctl net.ipv4.ip_local_port_range="1024 65535" \
214+
--network ${{ env.RUN_CFG__NETWORK_NAME }} \
215+
--name ${{ env.RUN_CFG__MACHINE2_NAME }} \
216+
"${{ env.RUN_CFG__DOCKER_IMAGE_NAME }}" \
217+
sleep infinity
218+
219+
echo "Waiting for container ${{ env.RUN_CFG__MACHINE2_NAME }} to start ..."
220+
221+
# Set the timeout using the attempt counter
222+
MAX_ATTEMPTS=50
223+
ATTEMPT=0
224+
225+
until [ "$(docker container inspect --format '{{.State.Running}}' "${{ env.RUN_CFG__MACHINE2_NAME }}")" = "true" ]; do
226+
ATTEMPT=$((ATTEMPT + 1))
227+
if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then
228+
echo "Error: Container did not start within the allotted time (timeout)!"
229+
exit 1
230+
fi
231+
sleep 0.2
232+
done
233+
234+
echo "Container successfully launched in $((ATTEMPT * 2 / 10)) second(s)!"
235+
236+
#
237+
# Setup authorized_keys
238+
#
239+
cat ${{ github.workspace }}/${{ env.RUN_CFG__SSH_KEY_NAME }}.pub | \
240+
docker exec -i -u test ${{ env.RUN_CFG__MACHINE2_NAME }} sh -c " \
241+
cat >> /home/test/.ssh/authorized_keys && \
242+
chmod 600 /home/test/.ssh/authorized_keys \
243+
"
244+
245+
#
246+
# Log status
247+
#
248+
docker exec -u root ${{ env.RUN_CFG__MACHINE2_NAME }} sh -c "
249+
echo '--- INSIDE CONTAINER OS ---' && \
250+
cat /etc/os-release && \
251+
echo '--- INSIDE CONTAINER .SSH ---' && \
252+
ls -la /home/test/.ssh/ \
253+
"
254+
255+
- name: Run machine1 (main test container)
256+
run: |
257+
# Launch the main container, passing the private key to the .ssh folder of the test user.
258+
# Pass environment variables so Python tests know where to start.
259+
# Added the --rm flag so that the container is automatically deleted after the tests are completed
260+
docker run --rm -t \
261+
${{ env.DOCKER_HIGHLOAD_FLAGS }} \
262+
--network ${{ env.RUN_CFG__NETWORK_NAME }} \
263+
--name ${{ env.RUN_CFG__MACHINE1_NAME }} \
264+
-v ${{ github.workspace }}/${{ env.RUN_CFG__LOGS_DIR }}:/home/test/testgres/logs \
265+
-v ${{ github.workspace }}/${{ env.RUN_CFG__SSH_KEY_NAME }}:/home/test/testgres/id_ed25519_test:ro \
266+
-e TEST_CFG__REMOTE_HOST="${{ env.RUN_CFG__MACHINE2_NAME }}" \
267+
-e TEST_CFG__REMOTE_PORT="22" \
268+
-e TEST_CFG__REMOTE_USERNAME="test" \
269+
-e TEST_CFG__REMOTE_SSH_KEY="/home/test/testgres/id_ed25519_test" \
270+
"${{ env.RUN_CFG__DOCKER_IMAGE_NAME }}"
165271
- name: Upload Logs
166272
uses: actions/upload-artifact@v7
167273
if: always() # IT IS IMPORTANT!

Dockerfile--alpine.tmpl

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
ARG PG_VERSION=17
2+
ARG PYTHON_VERSION=3.12
3+
4+
# --------------------------------------------- base1
5+
FROM postgres:${PG_VERSION}-alpine AS base1
6+
7+
RUN apk add --no-cache \
8+
coreutils \
9+
bash \
10+
mc \
11+
procps \
12+
openssh \
13+
sshpass \
14+
sudo \
15+
git
16+
17+
RUN sed -i 's/#MaxStartups 10:30:100/MaxStartups 2000:30:2000/' /etc/ssh/sshd_config && \
18+
sed -i 's/#MaxSessions 10/MaxSessions 500/' /etc/ssh/sshd_config && \
19+
sed -i 's/#MaxAuthTries 6/MaxAuthTries 20/' /etc/ssh/sshd_config
20+
21+
# --------------------------------------------- base2_with_python-3
22+
FROM base1 AS base2_with_python-3
23+
24+
RUN apk add --no-cache \
25+
curl \
26+
python3 \
27+
python3-dev \
28+
build-base \
29+
musl-dev \
30+
linux-headers \
31+
# For pyenv \
32+
patch \
33+
xz-dev \
34+
zip \
35+
zlib-dev \
36+
libffi-dev \
37+
readline-dev \
38+
openssl openssl-dev \
39+
sqlite-dev \
40+
bzip2-dev
41+
42+
ENV PYTHON_BINARY=python3
43+
44+
# --------------------------------------------- base3_with_python-3.7
45+
FROM base2_with_python-3 AS base3_with_python-3.7
46+
ENV PYTHON_VERSION=3.7
47+
48+
# --------------------------------------------- base3_with_python-3.8.0
49+
FROM base2_with_python-3 AS base3_with_python-3.8.0
50+
ENV PYTHON_VERSION=3.8.0
51+
52+
# --------------------------------------------- base3_with_python-3.8
53+
FROM base2_with_python-3 AS base3_with_python-3.8
54+
ENV PYTHON_VERSION=3.8
55+
56+
# --------------------------------------------- base3_with_python-3.9
57+
FROM base2_with_python-3 AS base3_with_python-3.9
58+
ENV PYTHON_VERSION=3.9
59+
60+
# --------------------------------------------- base3_with_python-3.10
61+
FROM base2_with_python-3 AS base3_with_python-3.10
62+
ENV PYTHON_VERSION=3.10
63+
64+
# --------------------------------------------- base3_with_python-3.11
65+
FROM base2_with_python-3 AS base3_with_python-3.11
66+
ENV PYTHON_VERSION=3.11
67+
68+
# --------------------------------------------- base3_with_python-3.12
69+
FROM base2_with_python-3 AS base3_with_python-3.12
70+
ENV PYTHON_VERSION=3.12
71+
72+
# --------------------------------------------- base3_with_python-3.13
73+
FROM base2_with_python-3 AS base3_with_python-3.13
74+
ENV PYTHON_VERSION=3.13
75+
76+
# --------------------------------------------- base3_with_python-3.14
77+
FROM base2_with_python-3 AS base3_with_python-3.14
78+
ENV PYTHON_VERSION=3.14
79+
80+
# --------------------------------------------- final
81+
FROM base3_with_python-${PYTHON_VERSION} AS final
82+
83+
EXPOSE 22
84+
RUN ssh-keygen -A
85+
86+
RUN adduser -D test && addgroup -S sudo && adduser test sudo
87+
88+
# It allows to use sudo without password
89+
RUN echo "test ALL=(ALL:ALL) NOPASSWD:ALL" >> /etc/sudoers
90+
91+
# THIS CMD IS NEEDED TO CONNECT THROUGH SSH WITHOUT PASSWORD
92+
RUN echo "test:*" | chpasswd -e
93+
94+
USER test
95+
RUN curl https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bash
96+
RUN ~/.pyenv/bin/pyenv install ${PYTHON_VERSION}
97+
USER root
98+
99+
COPY --chown=test:test . /home/test/testgres
100+
WORKDIR /home/test/testgres
101+
102+
ENV LANG=C.UTF-8
103+
104+
RUN chmod 700 /home/test/ && \
105+
mkdir -p /home/test/.ssh && \
106+
chown -R test:test /home/test/.ssh && \
107+
mkdir /home/test/testgres/logs && \
108+
chown -R test:test /home/test/testgres/logs
109+
110+
#
111+
# \"$@\"
112+
# - quote is important!
113+
# - "DUMMY-DUMMY-DUMMY" will be ignored. Do not ask me "why?". AXEZ.
114+
#
115+
ENTRYPOINT ["sh", "-c", " \
116+
set -eux; \
117+
echo 'SYSTEM START: PREPARING SSH'; \
118+
/usr/sbin/sshd; \
119+
ls -la /home/test/.ssh/; \
120+
\"$@\" \
121+
", "DUMMY-DUMMY-DUMMY"]
122+
123+
# Run tests by default (master machine role)
124+
CMD ["bash", "-c", " \
125+
set -eux; \
126+
echo \"HOME DIR IS [`realpath ~/`]\"; \
127+
echo \"WORK DIR IS [$(pwd)]\"; \
128+
if [ ! -f /home/test/.ssh/id_rsa ]; then \
129+
su test -c \"ssh-keygen -t rsa -f /home/test/.ssh/id_rsa -q -N ''\"; \
130+
su test -c \"cat /home/test/.ssh/id_rsa.pub >> /home/test/.ssh/authorized_keys\"; \
131+
chmod 600 /home/test/.ssh/authorized_keys; \
132+
fi; \
133+
ls -la /home/test/.ssh/; \
134+
su test -c \"ssh-keyscan -H localhost >> /home/test/.ssh/known_hosts\"; \
135+
su test -c \"ssh-keyscan -H 127.0.0.1 >> /home/test/.ssh/known_hosts\"; \
136+
if [ -n \"${TEST_CFG__REMOTE_HOST:-}\" ]; then \
137+
su test -c \"ssh-keyscan -H ${TEST_CFG__REMOTE_HOST} >> /home/test/.ssh/known_hosts\"; \
138+
fi; \
139+
if [ -n \"${TEST_CFG__REMOTE_SSH_KEY:-}\" ]; then \
140+
cp \"${TEST_CFG__REMOTE_SSH_KEY}\" \"${TEST_CFG__REMOTE_SSH_KEY}_ci\"; \
141+
export TEST_CFG__REMOTE_SSH_KEY=\"${TEST_CFG__REMOTE_SSH_KEY}_ci\"; \
142+
chown test:test \"${TEST_CFG__REMOTE_SSH_KEY}\"; \
143+
chmod 600 \"${TEST_CFG__REMOTE_SSH_KEY}\"; \
144+
ls -la /home/test/.ssh/; \
145+
fi; \
146+
ls -la ./; \
147+
su test -c \"TEST_FILTER='' PATH=\"/home/test/.pyenv/bin:$PATH\" bash ./run_tests2.sh\"; \
148+
"]

0 commit comments

Comments
 (0)