Skip to content

Commit e9eba34

Browse files
committed
add github workflow for linux with pytest and pebble
Adds a workflow on ubuntu-latest. Required some adjustements: - no longer use `apachectl` and call `httpd/apache2` directly - add some more debuging when apache start/restart/stop fails
1 parent d7232c9 commit e9eba34

34 files changed

+501
-375
lines changed

.github/workflows/linux.yml

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,33 +39,62 @@ env:
3939
CFLAGS: "-g"
4040

4141
jobs:
42-
build:
42+
linux:
4343
name: ${{ matrix.name }}
4444
runs-on: ubuntu-latest
4545
timeout-minutes: 30
4646
strategy:
4747
fail-fast: false
4848
matrix:
49-
include:
49+
build:
5050
- name: Default
5151
install_packages:
52-
install_steps: pytest
52+
install_steps: pytest pebble
5353

54-
steps:
55-
- name: 'install prereqs'
56-
run: |
57-
sudo apt-get update -y
58-
sudo apt-get install -y --no-install-suggests --no-install-recommends \
59-
libtool autoconf automake pkgconf apache2 apache2-dev openssl \
60-
libssl-dev libjansson-dev libcurl4-openssl-dev \
61-
${{ matrix.build.install_packages }}
62-
python3 -m venv $HOME/venv
54+
steps:
55+
- name: 'install prereqs'
56+
run: |
57+
sudo apt-get update -y
58+
sudo apt-get install -y --no-install-suggests --no-install-recommends \
59+
libtool autoconf automake pkgconf apache2 apache2-dev openssl \
60+
curl nghttp2-client libssl-dev libjansson-dev libcurl4-openssl-dev \
61+
${{ matrix.build.install_packages }}
62+
python3 -m venv $HOME/venv
6363
64-
- name: 'configure'
65-
run: |
66-
autoreconf -fi
67-
./configure --enable-werror
64+
- uses: actions/checkout@v4
6865

69-
- name: 'build'
70-
run: |
71-
make V=1
66+
- name: 'install test prereqs'
67+
run: |
68+
[ -x "$HOME/venv/bin/activate" ] && source $HOME/venv/bin/activate
69+
python3 -m pip install -r test/requirements.txt
70+
71+
- name: setup Go
72+
if: contains(matrix.build.install_steps, 'pebble')
73+
uses: actions/setup-go@v5
74+
75+
- name: install pebble
76+
if: contains(matrix.build.install_steps, 'pebble')
77+
run: |
78+
export PATH=$PATH:$HOME/go/bin
79+
git clone --quiet --depth=1 https://github.com/letsencrypt/pebble/
80+
cd pebble
81+
go install ./cmd/pebble
82+
go install ./cmd/pebble-challtestsrv
83+
84+
- name: 'configure'
85+
run: |
86+
export PATH=$PATH:$HOME/go/bin
87+
autoreconf -fi
88+
./configure --enable-werror
89+
90+
- name: 'build'
91+
run: make V=1
92+
93+
- name: pytest
94+
if: contains(matrix.build.install_steps, 'pytest')
95+
env:
96+
PYTEST_ADDOPTS: "--color=yes"
97+
run: |
98+
export PATH=$PATH:$HOME/go/bin
99+
[ -x "$HOME/venv/bin/activate" ] && source $HOME/venv/bin/activate
100+
pytest -v

configure.ac

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -117,24 +117,37 @@ CPPFLAGS="-I$($APXS -q includedir) -I$($APXS -q APR_INCLUDEDIR) $($APXS -q EXTRA
117117
HTTPD_VERSION="$($APXS -q HTTPD_VERSION)"
118118
AC_SUBST(HTTPD_VERSION)
119119

120-
APACHECTL="$sbindir/apachectl"
121-
if test ! -x "$APACHECTL"; then
122-
# rogue distros rename things! =)
123-
APACHECTL="$sbindir/apache2ctl"
124-
fi
125-
AC_SUBST(APACHECTL)
126-
127-
if test -x "$APACHECTL"; then
128-
DSO_MODULES="$($APACHECTL -t -D DUMP_MODULES | fgrep '(shared)'| sed 's/_module.*//g'|tr -d \\n)"
129-
AC_SUBST(DSO_MODULES)
130-
STATIC_MODULES="$($APACHECTL -t -D DUMP_MODULES | fgrep '(static)'| sed 's/_module.*//g'|tr -d \\n)"
131-
AC_SUBST(STATIC_MODULES)
132-
MPM_MODULES="mpm_event mpm_worker"
133-
AC_SUBST(MPM_MODULES)
120+
HTTPD="$sbindir/httpd"
121+
if test -x "$HTTPD"; then
122+
: # all fine
134123
else
135-
AC_MSG_WARN("apachectl not found in '$BINDIR', test suite will not work!")
136-
APACHECTL=""
124+
HTTPD="$sbindir/apache2"
125+
if test -x "$HTTPD"; then
126+
: # all fine
127+
else
128+
HTTPD=""
129+
AC_PATH_PROG([HTTPD], [httpd])
130+
if test -x "$HTTPD"; then
131+
: # ok
132+
else
133+
HTTPD=""
134+
AC_PATH_PROG([HTTPD], [apache2])
135+
if test -x "$HTTPD"; then
136+
: # ok
137+
else
138+
AC_MSG_ERROR([httpd/apache2 not in PATH])
139+
fi
140+
fi
141+
fi
137142
fi
143+
AC_SUBST(HTTPD)
144+
145+
DSO_MODULES="$($HTTPD -t -D DUMP_MODULES | fgrep '(shared)'| sed 's/_module.*//g'|tr -d \\n)"
146+
AC_SUBST(DSO_MODULES)
147+
STATIC_MODULES="$($HTTPD -t -D DUMP_MODULES | fgrep '(static)'| sed 's/_module.*//g'|tr -d \\n)"
148+
AC_SUBST(STATIC_MODULES)
149+
MPM_MODULES="mpm_event mpm_worker"
150+
AC_SUBST(MPM_MODULES)
138151

139152
# We need a JSON lib, like jansson
140153
#
@@ -459,6 +472,7 @@ AC_MSG_NOTICE([summary of build options:
459472
Install prefix: ${prefix}
460473
APXS: ${APXS}
461474
HTTPD-VERSION: ${HTTPD_VERSION}
475+
HTTPD: ${HTTPD}
462476
C compiler: ${CC} ${COMPILER_VERSION}
463477
CFLAGS: ${CFLAGS}
464478
WARNCFLAGS: ${WERROR_CFLAGS}

test/modules/md/conftest.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,11 @@
55

66
sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
77

8-
from .md_conf import HttpdConf
98
from .md_env import MDTestEnv
109
from .md_acme import MDPebbleRunner, MDBoulderRunner
1110

1211

13-
def pytest_report_header(config, startdir):
12+
def pytest_report_header(config):
1413
env = MDTestEnv()
1514
return "mod_md: [apache: {aversion}({prefix}), mod_{ssl}, ACME server: {acme}]".format(
1615
prefix=env.prefix,

test/modules/md/md_env.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ class MDTestSetup(HttpdTestSetup):
2727
def __init__(self, env: 'MDTestEnv'):
2828
super().__init__(env=env)
2929
self.mdenv = env
30-
self.add_modules(["watchdog", "proxy_connect", "md"])
30+
self.add_modules(["watchdog", "proxy_connect"])
31+
self.add_local_module("md", "src/.libs/mod_md.so")
3132

3233
def make(self):
3334
super().make()
@@ -87,13 +88,16 @@ def is_pebble(cls) -> bool:
8788
def lacks_ocsp(cls):
8889
return cls.is_pebble()
8990

91+
A2MD_BIN = None
92+
9093
@classmethod
9194
def has_a2md(cls):
92-
d = os.path.dirname(inspect.getfile(HttpdTestEnv))
93-
config = ConfigParser(interpolation=ExtendedInterpolation())
94-
config.read(os.path.join(d, 'config.ini'))
95-
bin_dir = config.get('global', 'bindir')
96-
a2md_bin = os.path.join(bin_dir, 'a2md')
95+
if cls.A2MD_BIN is None:
96+
d = os.path.dirname(inspect.getfile(HttpdTestEnv))
97+
config = ConfigParser(interpolation=ExtendedInterpolation())
98+
config.read(os.path.join(d, 'config.ini'))
99+
src_dir = config.get('test', 'src_dir')
100+
a2md_bin = os.path.join(src_dir, 'src/a2md')
97101
return os.path.isfile(a2md_bin)
98102

99103
def __init__(self, pytestconfig=None):
@@ -113,7 +117,7 @@ def __init__(self, pytestconfig=None):
113117
self._acme_server_down = False
114118
self._acme_server_ok = False
115119

116-
self._a2md_bin = os.path.join(self.bin_dir, 'a2md')
120+
self._a2md_bin = os.path.join(self.src_dir, 'src/a2md')
117121
self._default_domain = f"test1.{self.http_tld}"
118122
self._tailscale_domain = "test.headless-chicken.ts.net"
119123
self._store_dir = "./md"
@@ -309,8 +313,15 @@ def check_md(self, domain, md=None, state=-1, ca=None, protocol=None, agreement=
309313
if md:
310314
domain = md
311315
path = self.store_domain_file(domain, 'md.json')
312-
with open(path) as f:
313-
md = json.load(f)
316+
try:
317+
with open(path) as f:
318+
md = json.load(f)
319+
except FileNotFoundError:
320+
log.error(f"not found: {path}")
321+
if not os.path.exists(self._store_dir):
322+
log.error(f"md store dir not found: {self._store_dir}")
323+
self.httpd_error_log.dump(log)
324+
assert False
314325
assert md
315326
if domains:
316327
assert md['domains'] == domains

test/modules/md/test_010_store_migrate.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class TestStoreMigrate:
1313
@pytest.fixture(autouse=True, scope='class')
1414
def _class_scope(self, env):
1515
MDConf(env).install()
16-
assert env.apache_restart() == 0
16+
assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
1717

1818
# install old store, start a2md list, check files afterwards
1919
def test_md_010_000(self, env):

test/modules/md/test_202_acmev2_regs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def _class_scope(self, env, acme):
1919
env.check_acme()
2020
env.APACHE_CONF_SRC = "data/test_drive"
2121
MDConf(env).install()
22-
assert env.apache_restart() == 0
22+
assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
2323

2424
@pytest.fixture(autouse=True, scope='function')
2525
def _method_scope(self, env):

test/modules/md/test_300_conf_validate.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def test_md_300_001(self, env):
2424
MDConf(env, text="""
2525
MDomain not-forbidden.org www.not-forbidden.org mail.not-forbidden.org
2626
""").install()
27-
assert env.apache_restart() == 0
27+
assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
2828
#
2929
env.httpd_error_log.ignore_recent(
3030
lognos = [
@@ -38,7 +38,7 @@ def test_md_300_002(self, env):
3838
MDomain not-forbidden.org www.not-forbidden.org mail.not-forbidden.org
3939
MDomain example2.org www.example2.org mail.example2.org
4040
""").install()
41-
assert env.apache_restart() == 0
41+
assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
4242
#
4343
env.httpd_error_log.ignore_recent(
4444
lognos = [
@@ -84,7 +84,7 @@ def test_md_300_005(self, env):
8484
MDomain example2.org www.example2.org www.example3.org
8585
</VirtualHost>
8686
""").install()
87-
assert env.apache_restart() == 0
87+
assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
8888
#
8989
env.httpd_error_log.ignore_recent(
9090
lognos = [
@@ -101,7 +101,7 @@ def test_md_300_006(self, env):
101101
MDomain example2.org www.example2.org www.example3.org
102102
</VirtualHost>
103103
""").install()
104-
assert env.apache_restart() == 0
104+
assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
105105
#
106106
env.httpd_error_log.ignore_recent(
107107
lognos = [
@@ -121,7 +121,7 @@ def test_md_300_007(self, env):
121121
ServerName www.example2.org
122122
</VirtualHost>
123123
""").install()
124-
assert env.apache_restart() == 0
124+
assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
125125
#
126126
env.httpd_error_log.ignore_recent(
127127
lognos = [
@@ -144,7 +144,7 @@ def test_md_300_008(self, env):
144144
ServerAlias example2.org
145145
</VirtualHost>
146146
""").install()
147-
assert env.apache_restart() == 0
147+
assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
148148
#
149149
env.httpd_error_log.ignore_recent(
150150
lognos = [
@@ -186,7 +186,7 @@ def test_md_300_010(self, env):
186186
</VirtualHost>
187187
""")
188188
conf.install()
189-
assert env.apache_restart() == 0
189+
assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
190190

191191
# test case: MDomain, misses one ServerAlias
192192
def test_md_300_011a(self, env):
@@ -216,7 +216,7 @@ def test_md_300_011b(self, env):
216216
ServerAlias test4.not-forbidden.org
217217
</VirtualHost>
218218
""" % env.https_port).install()
219-
assert env.apache_restart() == 0
219+
assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
220220

221221
# test case: MDomain does not match any vhost
222222
def test_md_300_012(self, env):
@@ -227,7 +227,7 @@ def test_md_300_012(self, env):
227227
ServerAlias test3.not-forbidden.org
228228
</VirtualHost>
229229
""").install()
230-
assert env.apache_restart() == 0
230+
assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
231231
#
232232
env.httpd_error_log.ignore_recent(
233233
lognos = [
@@ -246,7 +246,7 @@ def test_md_300_013(self, env):
246246
ServerName test-b.example2.org
247247
</VirtualHost>
248248
""").install()
249-
assert env.apache_restart() == 0
249+
assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
250250

251251
# test case: global server name as managed domain name
252252
def test_md_300_014(self, env):
@@ -257,7 +257,7 @@ def test_md_300_014(self, env):
257257
ServerName www.example2.org
258258
</VirtualHost>
259259
""").install()
260-
assert env.apache_restart() == 0
260+
assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
261261

262262
# test case: valid pkey specification
263263
def test_md_300_015(self, env):
@@ -268,7 +268,7 @@ def test_md_300_015(self, env):
268268
MDPrivateKeys RSA 3072
269269
MDPrivateKeys RSA 4096
270270
""").install()
271-
assert env.apache_restart() == 0
271+
assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
272272

273273
# test case: invalid pkey specification
274274
@pytest.mark.parametrize("line,exp_err_msg", [
@@ -355,7 +355,7 @@ def test_md_300_022(self, env):
355355
ServerName secret.com
356356
</VirtualHost>
357357
""").install()
358-
assert env.apache_restart() == 0
358+
assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
359359
#
360360
env.httpd_error_log.ignore_recent(
361361
lognos = [
@@ -431,7 +431,7 @@ def test_md_300_026(self, env):
431431
</VirtualHost>
432432
""")
433433
conf.install()
434-
assert env.apache_restart() == 0
434+
assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
435435

436436
# test case: configure more than 1 CA
437437
@pytest.mark.parametrize("cas, should_work", [
@@ -493,7 +493,7 @@ def test_md_300_028(self, env):
493493
# It works, if we only match on ServerNames
494494
conf.add("MDMatchNames servernames")
495495
conf.install()
496-
assert env.apache_restart() == 0
496+
assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
497497
env.httpd_error_log.ignore_recent(
498498
lognos=[
499499
"AH10040", # ServerAlias not covered
@@ -537,7 +537,7 @@ def test_md_300_029(self, env):
537537
# It works, if we only match on ServerNames
538538
conf.add("MDMatchNames servernames")
539539
conf.install()
540-
assert env.apache_restart() == 0
540+
assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
541541
time.sleep(2)
542542
assert env.apache_stop() == 0
543543
# we need dns-01 challenge for the wildcard, which is not configured

0 commit comments

Comments
 (0)