Commit 38aabd7a authored by Jérome Perrin's avatar Jérome Perrin

Update Release Candidate

parents 1eb6911c daadef35
...@@ -48,6 +48,7 @@ recipe = zc.recipe.egg:develop ...@@ -48,6 +48,7 @@ recipe = zc.recipe.egg:develop
setup = ${ZEO4-repository:location} setup = ${ZEO4-repository:location}
egg = ZEO egg = ZEO
egg-versions = egg-versions =
ZEO = 4.3.1
[ZEO4-repository] [ZEO4-repository]
recipe = slapos.recipe.build:gitclone recipe = slapos.recipe.build:gitclone
......
...@@ -24,6 +24,7 @@ extends = ...@@ -24,6 +24,7 @@ extends =
../icu/buildout.cfg ../icu/buildout.cfg
../openssl/buildout.cfg ../openssl/buildout.cfg
../libnsl/buildout.cfg ../libnsl/buildout.cfg
../libsodium/buildout.cfg
../sqlite3/buildout.cfg ../sqlite3/buildout.cfg
../oniguruma/buildout.cfg ../oniguruma/buildout.cfg
../xz-utils/buildout.cfg ../xz-utils/buildout.cfg
...@@ -78,9 +79,12 @@ configure-options = ...@@ -78,9 +79,12 @@ configure-options =
--enable-mbstring --enable-mbstring
--enable-pcntl --enable-pcntl
--enable-session --enable-session
--enable-sysvsem
--with-apxs2=${apache:location}/bin/apxs --with-apxs2=${apache:location}/bin/apxs
--with-bz2=${bzip2:location} --with-bz2=${bzip2:location}
--with-curl --with-curl
--with-freetype
--with-jpeg
--with-gettext=${gettext:location} --with-gettext=${gettext:location}
--with-imap-ssl --with-imap-ssl
--with-imap=${cclient:location} --with-imap=${cclient:location}
...@@ -88,6 +92,7 @@ configure-options = ...@@ -88,6 +92,7 @@ configure-options =
--with-mysqli=mysqlnd --with-mysqli=mysqlnd
--with-openssl=${openssl:location} --with-openssl=${openssl:location}
--with-pdo-mysql=mysqlnd --with-pdo-mysql=mysqlnd
--with-sodium=${libsodium:location}
--with-zip --with-zip
--with-zlib --with-zlib
...@@ -95,7 +100,7 @@ configure-options = ...@@ -95,7 +100,7 @@ configure-options =
# It will create a pear/temp directory under the SR instead of a shared /tmp/pear/temp. # It will create a pear/temp directory under the SR instead of a shared /tmp/pear/temp.
# XXX we could mkdir tmp there # XXX we could mkdir tmp there
environment = environment =
PKG_CONFIG_PATH=${libxml2:location}/lib/pkgconfig:${openssl:location}/lib/pkgconfig:${libzip:location}/lib/pkgconfig:${sqlite3:location}/lib/pkgconfig:${curl:location}/lib/pkgconfig:${icu:location}/lib/pkgconfig:${oniguruma:location}/lib/pkgconfig:${argon2:location}/lib/pkgconfig:${zlib:location}/lib/pkgconfig:${mariadb:location}/lib/pkgconfig:${libjpeg:location}/lib/pkgconfig:${libpng:location}/lib/pkgconfig:${freetype:location}/lib/pkgconfig:${libiconv:location}/lib/pkgconfig:${libzip:location}/lib/pkgconfig PKG_CONFIG_PATH=${libxml2:location}/lib/pkgconfig:${openssl:location}/lib/pkgconfig:${libzip:location}/lib/pkgconfig:${sqlite3:location}/lib/pkgconfig:${curl:location}/lib/pkgconfig:${icu:location}/lib/pkgconfig:${oniguruma:location}/lib/pkgconfig:${argon2:location}/lib/pkgconfig:${zlib:location}/lib/pkgconfig:${mariadb:location}/lib/pkgconfig:${libjpeg:location}/lib/pkgconfig:${libpng:location}/lib/pkgconfig:${freetype:location}/lib/pkgconfig:${libiconv:location}/lib/pkgconfig:${libzip:location}/lib/pkgconfig:${libsodium:location}/lib/pkgconfig
PATH=${pkgconfig:location}/bin:${bzip2:location}/bin:${libxml2:location}/bin:${xz-utils:location}/bin:%(PATH)s PATH=${pkgconfig:location}/bin:${bzip2:location}/bin:${libxml2:location}/bin:${xz-utils:location}/bin:%(PATH)s
CPPFLAGS=-I${libzip:location}/include CPPFLAGS=-I${libzip:location}/include
LDFLAGS=-L${bzip2:location}/lib -Wl,-rpath -Wl,${bzip2:location}/lib -Wl,-rpath -Wl,${curl:location}/lib -L${libtool:location}/lib -Wl,-rpath -Wl,${libtool:location}/lib -L${mariadb:location}/lib -Wl,-rpath -Wl,${mariadb:location}/lib -L${zlib:location}/lib -Wl,-rpath -Wl,${zlib:location}/lib -L${libzip:location}/lib -Wl,-rpath -Wl,${libzip:location}/lib -L${argon2:location}/lib/x86_64-linux-gnu -Wl,-rpath -Wl,${argon2:location}/lib/x86_64-linux-gnu -Wl,-rpath -Wl,${zstd:location}/lib -L${libnsl:location}/lib -Wl,-rpath -Wl,${libnsl:location}/lib -L${sqlite3:location}/lib -Wl,-rpath -Wl,${sqlite3:location}/lib LDFLAGS=-L${bzip2:location}/lib -Wl,-rpath -Wl,${bzip2:location}/lib -Wl,-rpath -Wl,${curl:location}/lib -L${libtool:location}/lib -Wl,-rpath -Wl,${libtool:location}/lib -L${mariadb:location}/lib -Wl,-rpath -Wl,${mariadb:location}/lib -L${zlib:location}/lib -Wl,-rpath -Wl,${zlib:location}/lib -L${libzip:location}/lib -Wl,-rpath -Wl,${libzip:location}/lib -L${argon2:location}/lib/x86_64-linux-gnu -Wl,-rpath -Wl,${argon2:location}/lib/x86_64-linux-gnu -Wl,-rpath -Wl,${zstd:location}/lib -L${libnsl:location}/lib -Wl,-rpath -Wl,${libnsl:location}/lib -L${sqlite3:location}/lib -Wl,-rpath -Wl,${sqlite3:location}/lib
......
...@@ -5,21 +5,24 @@ ...@@ -5,21 +5,24 @@
[buildout] [buildout]
extends = extends =
../chromium/buildout.cfg ../chromium/buildout.cfg
../nss/buildout.cfg ../glib/buildout.cfg
../nspr/buildout.cfg ../nspr/buildout.cfg
../nss/buildout.cfg
../pcre2/buildout.cfg
../xorg/buildout.cfg ../xorg/buildout.cfg
parts = parts =
chromedriver-wrapper chromedriver-wrapper
[chromedriver-wrapper-120]
<= chromedriver-wrapper
part = chromedriver-120
[chromedriver-wrapper-91] [chromedriver-wrapper-91]
<= chromedriver-wrapper <= chromedriver-wrapper
part = chromedriver-91 part = chromedriver-91
[chromedriver-wrapper-2.41]
<= chromedriver-wrapper
part = chromedriver-2.41
[chromedriver-wrapper] [chromedriver-wrapper]
# generate a wrapper named ${:wrapper-name} setting $LD_LIBRARY_PATH # generate a wrapper named ${:wrapper-name} setting $LD_LIBRARY_PATH
...@@ -42,30 +45,36 @@ install = ...@@ -42,30 +45,36 @@ install =
[chromedriver] [chromedriver]
<= chromedriver-91 <= chromedriver-120
[chromedriver-2.41] [chromedriver-120]
<= chromedriver-download <= chromedriver-download
version = 2.41 version = 120.0.6099.109
# Supports Chrome v67-69 revision-x86_64 = 1217362
md5sum-x86_64 = fbd8b9561575054e0e7e9cc53b680a70 generation-x86_64 = 1698717838856458
md5sum-x86_64 = 5cb8d386f01052cfc58c80ec63477db0
[chromedriver-91] [chromedriver-91]
<= chromedriver-download <= chromedriver-download
url = https://chromedriver.storage.googleapis.com/${:version}/chromedriver_linux64.zip
version = 91.0.4472.101 version = 91.0.4472.101
# Supports Chrome v91
md5sum-x86_64 = cc43ba0babbfff7f22b48165ec8e8c81 md5sum-x86_64 = cc43ba0babbfff7f22b48165ec8e8c81
[chromedriver-download] [chromedriver-download]
# Installs chromedriver ${version}. # Installs chromedriver ${version}.
# This chromedriver is not usable directly, it needs a wrapper. # This chromedriver is not usable directly, it needs a wrapper.
recipe = slapos.recipe.build:download-unpacked recipe = slapos.recipe.build:download-unpacked
url = https://chromedriver.storage.googleapis.com/${:version}/chromedriver_${:_url}.zip
library = library =
${nss:location}/lib ${glib:location}/lib
${nspr:location}/lib
${libX11:location}/lib ${libX11:location}/lib
${libXau:location}/lib
${libxcb:location}/lib
${libXdmcp:location}/lib
${nspr:location}/lib
${nss:location}/lib
${pcre2:location}/lib
[chromedriver-download:getattr(sys,'_multiarch',None)=='x86_64-linux-gnu'] [chromedriver-download:getattr(sys,'_multiarch',None)=='x86_64-linux-gnu']
_url = linux64 url = https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2F${:revision-x86_64}%2Fchromedriver_linux64.zip?generation=${:generation-x86_64}&alt=media
md5sum = ${:md5sum-x86_64} md5sum = ${:md5sum-x86_64}
...@@ -65,36 +65,35 @@ install = ...@@ -65,36 +65,35 @@ install =
)) ))
os.fchmod(f.fileno(), 0o755) os.fchmod(f.fileno(), 0o755)
[chromium-wrapper-120]
<= chromium-wrapper
part = chromium-120
[chromium-wrapper-91] [chromium-wrapper-91]
<= chromium-wrapper <= chromium-wrapper
part = chromium-91 part = chromium-91
[chromium-wrapper-69] [chromium]
<= chromium-wrapper <= chromium-120
part = chromium-69
[chromium-120]
<= chromium-download
version = 120.0.6099.109
revision-x86_64 = 1217362
md5sum-x86_64 = 86719e40f3d33f1b421d073bb4a71f41
generation-x86_64 = 1698717835110888
[chromium]
<= chromium-91
[chromium-91] [chromium-91]
<= chromium-download <= chromium-download
version = 91.0.4472.114 version = 91.0.4472.114
revision_x86-64 = 870763 revision-x86_64 = 870763
md5sum-x86_64 = 74eab41580469c2b8117cf396db823cb md5sum-x86_64 = 74eab41580469c2b8117cf396db823cb
generation-x86_64 = 1617926496067901 generation-x86_64 = 1617926496067901
[chromium-69]
<= chromium-download
version = 69.0.3497.0
revision_x86-64 = 576753
md5sum-x86_64 = 08ac27fd40ace4ca8dfbd1db403deccb
generation-x86_64 = 1532051976706023
[chromium-download] [chromium-download]
# macro to download a binary build of chromium and generate a # macro to download a binary build of chromium and generate a
# wrapper as chrome-slapos in the part directory # wrapper as chrome-slapos in the part directory
...@@ -148,5 +147,5 @@ path = ...@@ -148,5 +147,5 @@ path =
${fontconfig:location}/bin ${fontconfig:location}/bin
[chromium-download:getattr(sys,'_multiarch',None)=='x86_64-linux-gnu'] [chromium-download:getattr(sys,'_multiarch',None)=='x86_64-linux-gnu']
url = https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2F${:revision_x86-64}%2Fchrome-linux.zip?generation=${:generation-x86_64}&alt=media url = https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2F${:revision-x86_64}%2Fchrome-linux.zip?generation=${:generation-x86_64}&alt=media
md5sum = ${:md5sum-x86_64} md5sum = ${:md5sum-x86_64}
From a02c80e17f794dc5eaea1c4edd3a2a3277a13638 Mon Sep 17 00:00:00 2001
From: Kazuhiko SHIOZAKI <kazuhiko@nexedi.com>
Date: Tue, 18 Jul 2023 10:26:54 +0200
Subject: [PATCH 1/4] Cast int to float in compare methods.
---
src/DateTime/DateTime.py | 30 ++++++++++-------------------
src/DateTime/tests/test_datetime.py | 17 ++++++++++++++++
2 files changed, 27 insertions(+), 20 deletions(-)
diff --git a/src/DateTime/DateTime.py b/src/DateTime/DateTime.py
index 2d2d97f..c141306 100644
--- a/src/DateTime/DateTime.py
+++ b/src/DateTime/DateTime.py
@@ -1256,12 +1256,10 @@ class DateTime(object):
"""
if t is None:
t = 0
- if isinstance(t, float):
+ if isinstance(t, (float, int)):
return self._micros > long(t * 1000000)
- try:
+ else:
return self._micros > t._micros
- except AttributeError:
- return self._micros > t
__gt__ = greaterThan
@@ -1279,12 +1277,10 @@ class DateTime(object):
"""
if t is None:
t = 0
- if isinstance(t, float):
+ if isinstance(t, (float, int)):
return self._micros >= long(t * 1000000)
- try:
+ else:
return self._micros >= t._micros
- except AttributeError:
- return self._micros >= t
__ge__ = greaterThanEqualTo
@@ -1301,12 +1297,10 @@ class DateTime(object):
"""
if t is None:
t = 0
- if isinstance(t, float):
+ if isinstance(t, (float, int)):
return self._micros == long(t * 1000000)
- try:
+ else:
return self._micros == t._micros
- except AttributeError:
- return self._micros == t
def notEqualTo(self, t):
"""Compare this DateTime object to another DateTime object
@@ -1348,12 +1342,10 @@ class DateTime(object):
"""
if t is None:
t = 0
- if isinstance(t, float):
+ if isinstance(t, (float, int)):
return self._micros < long(t * 1000000)
- try:
+ else:
return self._micros < t._micros
- except AttributeError:
- return self._micros < t
__lt__ = lessThan
@@ -1370,12 +1362,10 @@ class DateTime(object):
"""
if t is None:
t = 0
- if isinstance(t, float):
+ if isinstance(t, (float, int)):
return self._micros <= long(t * 1000000)
- try:
+ else:
return self._micros <= t._micros
- except AttributeError:
- return self._micros <= t
__le__ = lessThanEqualTo
diff --git a/src/DateTime/tests/test_datetime.py b/src/DateTime/tests/test_datetime.py
index 249e79a..e6b3f93 100644
--- a/src/DateTime/tests/test_datetime.py
+++ b/src/DateTime/tests/test_datetime.py
@@ -228,6 +228,23 @@ class DateTimeTests(unittest.TestCase):
self.assertTrue(dt.lessThanEqualTo(dt1))
self.assertTrue(dt.notEqualTo(dt1))
self.assertFalse(dt.equalTo(dt1))
+ # Compare a date to float
+ dt = DateTime(1.0)
+ self.assertFalse(dt.greaterThan(1.0))
+ self.assertTrue(dt.greaterThanEqualTo(1.0))
+ self.assertFalse(dt.lessThan(1.0))
+ self.assertTrue(dt.lessThanEqualTo(1.0))
+ self.assertFalse(dt.notEqualTo(1.0))
+ self.assertTrue(dt.equalTo(1.0))
+ # Compare a date to int
+ dt = DateTime(1)
+ self.assertEqual(dt, DateTime(1.0))
+ self.assertFalse(dt.greaterThan(1))
+ self.assertTrue(dt.greaterThanEqualTo(1))
+ self.assertFalse(dt.lessThan(1))
+ self.assertTrue(dt.lessThanEqualTo(1))
+ self.assertFalse(dt.notEqualTo(1))
+ self.assertTrue(dt.equalTo(1))
def test_compare_methods_none(self):
# Compare a date to None
--
2.40.1
From 892e66132025ab8c213e4be57dd10f0f8eec7e60 Mon Sep 17 00:00:00 2001
From: Kazuhiko SHIOZAKI <kazuhiko@nexedi.com>
Date: Fri, 14 Jul 2023 21:10:06 +0200
Subject: [PATCH 2/4] Fix compare methods between DateTime(0) and None (fix
#52).
This is a fixup commit of 'further py3 work' that changed the behaviour of compare methods between DateTime(0) and None.
Now None is less than any DateTime instance including DateTime(0), just same as DateTime 2.
---
src/DateTime/DateTime.py | 10 +++++-----
src/DateTime/tests/test_datetime.py | 14 +++++++-------
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/src/DateTime/DateTime.py b/src/DateTime/DateTime.py
index c141306..caf67e1 100644
--- a/src/DateTime/DateTime.py
+++ b/src/DateTime/DateTime.py
@@ -1255,7 +1255,7 @@ class DateTime(object):
long integer microseconds.
"""
if t is None:
- t = 0
+ return True
if isinstance(t, (float, int)):
return self._micros > long(t * 1000000)
else:
@@ -1276,7 +1276,7 @@ class DateTime(object):
long integer microseconds.
"""
if t is None:
- t = 0
+ return True
if isinstance(t, (float, int)):
return self._micros >= long(t * 1000000)
else:
@@ -1296,7 +1296,7 @@ class DateTime(object):
long integer microseconds.
"""
if t is None:
- t = 0
+ return False
if isinstance(t, (float, int)):
return self._micros == long(t * 1000000)
else:
@@ -1341,7 +1341,7 @@ class DateTime(object):
long integer microseconds.
"""
if t is None:
- t = 0
+ return False
if isinstance(t, (float, int)):
return self._micros < long(t * 1000000)
else:
@@ -1361,7 +1361,7 @@ class DateTime(object):
long integer microseconds.
"""
if t is None:
- t = 0
+ return False
if isinstance(t, (float, int)):
return self._micros <= long(t * 1000000)
else:
diff --git a/src/DateTime/tests/test_datetime.py b/src/DateTime/tests/test_datetime.py
index e6b3f93..1dd6c32 100644
--- a/src/DateTime/tests/test_datetime.py
+++ b/src/DateTime/tests/test_datetime.py
@@ -248,13 +248,13 @@ class DateTimeTests(unittest.TestCase):
def test_compare_methods_none(self):
# Compare a date to None
- dt = DateTime('1997/1/1')
- self.assertTrue(dt.greaterThan(None))
- self.assertTrue(dt.greaterThanEqualTo(None))
- self.assertFalse(dt.lessThan(None))
- self.assertFalse(dt.lessThanEqualTo(None))
- self.assertTrue(dt.notEqualTo(None))
- self.assertFalse(dt.equalTo(None))
+ for dt in (DateTime('1997/1/1'), DateTime(0)):
+ self.assertTrue(dt.greaterThan(None))
+ self.assertTrue(dt.greaterThanEqualTo(None))
+ self.assertFalse(dt.lessThan(None))
+ self.assertFalse(dt.lessThanEqualTo(None))
+ self.assertTrue(dt.notEqualTo(None))
+ self.assertFalse(dt.equalTo(None))
def test_pickle(self):
dt = DateTime()
--
2.40.1
From 4a9798072c87d2fe53b2e1e15b004ff982f9686a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Thu, 30 Nov 2023 06:19:54 +0100
Subject: [PATCH 3/4] Make it possible to pickle datetimes returned by
asdatetime
Fixes #58
---
src/DateTime/pytz_support.py | 5 +++++
src/DateTime/tests/test_datetime.py | 7 +++++++
2 files changed, 12 insertions(+)
diff --git a/src/DateTime/pytz_support.py b/src/DateTime/pytz_support.py
index 9ebf3db..e0746ea 100644
--- a/src/DateTime/pytz_support.py
+++ b/src/DateTime/pytz_support.py
@@ -199,9 +199,14 @@ for hour in range(0, 13):
_old_zmap['+%s00' % fhour] = 'GMT+%i' % hour
+def _p(zone):
+ return _numeric_timezones[zone]
+
+
def _static_timezone_factory(data):
zone = data[0]
cls = type(zone, (StaticTzInfo,), dict(
+ __reduce__=lambda _: (_p, (zone, )),
zone=zone,
_utcoffset=memorized_timedelta(data[5][0][0]),
_tzname=data[6][:-1])) # strip the trailing null
diff --git a/src/DateTime/tests/test_datetime.py b/src/DateTime/tests/test_datetime.py
index 1dd6c32..b9eeea9 100644
--- a/src/DateTime/tests/test_datetime.py
+++ b/src/DateTime/tests/test_datetime.py
@@ -270,6 +270,13 @@ class DateTimeTests(unittest.TestCase):
for key in DateTime.__slots__:
self.assertEqual(getattr(dt, key), getattr(new, key))
+ def test_pickle_asdatetime_with_tz(self):
+ dt = DateTime('2002/5/2 8:00am GMT+8')
+ data = pickle.dumps(dt.asdatetime(), 1)
+ new = DateTime(pickle.loads(data))
+ for key in DateTime.__slots__:
+ self.assertEqual(getattr(dt, key), getattr(new, key))
+
def test_pickle_with_numerical_tz(self):
for dt_str in ('2007/01/02 12:34:56.789 +0300',
'2007/01/02 12:34:56.789 +0430',
--
2.40.1
From 6ac321746ab86374871623ddaf414b7948325d22 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Sun, 3 Dec 2023 15:57:01 +0100
Subject: [PATCH 4/4] Repair equality comparison between DateTime instances and
other types
Fixes #60
---
src/DateTime/DateTime.py | 20 +++++++++++++++-----
src/DateTime/tests/test_datetime.py | 17 +++++++++++++++++
2 files changed, 32 insertions(+), 5 deletions(-)
diff --git a/src/DateTime/DateTime.py b/src/DateTime/DateTime.py
index caf67e1..84570b9 100644
--- a/src/DateTime/DateTime.py
+++ b/src/DateTime/DateTime.py
@@ -1258,8 +1258,10 @@ class DateTime(object):
return True
if isinstance(t, (float, int)):
return self._micros > long(t * 1000000)
- else:
+ try:
return self._micros > t._micros
+ except AttributeError:
+ return self._micros > t
__gt__ = greaterThan
@@ -1279,8 +1281,10 @@ class DateTime(object):
return True
if isinstance(t, (float, int)):
return self._micros >= long(t * 1000000)
- else:
+ try:
return self._micros >= t._micros
+ except AttributeError:
+ return self._micros >= t
__ge__ = greaterThanEqualTo
@@ -1299,8 +1303,10 @@ class DateTime(object):
return False
if isinstance(t, (float, int)):
return self._micros == long(t * 1000000)
- else:
+ try:
return self._micros == t._micros
+ except AttributeError:
+ return self._micros == t
def notEqualTo(self, t):
"""Compare this DateTime object to another DateTime object
@@ -1344,8 +1350,10 @@ class DateTime(object):
return False
if isinstance(t, (float, int)):
return self._micros < long(t * 1000000)
- else:
+ try:
return self._micros < t._micros
+ except AttributeError:
+ return self._micros < t
__lt__ = lessThan
@@ -1364,8 +1372,10 @@ class DateTime(object):
return False
if isinstance(t, (float, int)):
return self._micros <= long(t * 1000000)
- else:
+ try:
return self._micros <= t._micros
+ except AttributeError:
+ return self._micros <= t
__le__ = lessThanEqualTo
diff --git a/src/DateTime/tests/test_datetime.py b/src/DateTime/tests/test_datetime.py
index b9eeea9..970a072 100644
--- a/src/DateTime/tests/test_datetime.py
+++ b/src/DateTime/tests/test_datetime.py
@@ -230,6 +230,8 @@ class DateTimeTests(unittest.TestCase):
self.assertFalse(dt.equalTo(dt1))
# Compare a date to float
dt = DateTime(1.0)
+ self.assertTrue(dt == DateTime(1.0)) # testing __eq__
+ self.assertFalse(dt != DateTime(1.0)) # testing __ne__
self.assertFalse(dt.greaterThan(1.0))
self.assertTrue(dt.greaterThanEqualTo(1.0))
self.assertFalse(dt.lessThan(1.0))
@@ -239,12 +241,27 @@ class DateTimeTests(unittest.TestCase):
# Compare a date to int
dt = DateTime(1)
self.assertEqual(dt, DateTime(1.0))
+ self.assertTrue(dt == DateTime(1)) # testing __eq__
+ self.assertFalse(dt != DateTime(1)) # testing __ne__
self.assertFalse(dt.greaterThan(1))
self.assertTrue(dt.greaterThanEqualTo(1))
self.assertFalse(dt.lessThan(1))
self.assertTrue(dt.lessThanEqualTo(1))
self.assertFalse(dt.notEqualTo(1))
self.assertTrue(dt.equalTo(1))
+ # Compare a date to string; there is no implicit type conversion
+ # but behavior if consistent as when comparing, for example, an int
+ # and a string.
+ dt = DateTime("2023")
+ self.assertFalse(dt == "2023") # testing __eq__
+ self.assertTrue(dt != "2023") # testing __ne__
+ if sys.version_info > (3, ):
+ self.assertRaises(TypeError, dt.greaterThan, "2023")
+ self.assertRaises(TypeError, dt.greaterThanEqualTo, "2023")
+ self.assertRaises(TypeError, dt.lessThan, "2023")
+ self.assertRaises(TypeError, dt.lessThanEqualTo, "2023")
+ self.assertTrue(dt.notEqualTo("2023"))
+ self.assertFalse(dt.equalTo("2023"))
def test_compare_methods_none(self):
# Compare a date to None
--
2.40.1
...@@ -61,17 +61,9 @@ install = ...@@ -61,17 +61,9 @@ install =
<= firefox-wrapper <= firefox-wrapper
part = firefox-115 part = firefox-115
[firefox-wrapper-78] [firefox-wrapper-102]
<= firefox-wrapper <= firefox-wrapper
part = firefox-78 part = firefox-102
[firefox-wrapper-68]
<= firefox-wrapper
part = firefox-68
[firefox-wrapper-60]
<= firefox-wrapper
part = firefox-60
[firefox-default-fonts-conf] [firefox-default-fonts-conf]
recipe = slapos.recipe.template:jinja2 recipe = slapos.recipe.template:jinja2
...@@ -103,23 +95,12 @@ version = 115.3.1esr ...@@ -103,23 +95,12 @@ version = 115.3.1esr
i686-md5sum = f0df1b5cce1edd65addc823da02f9488 i686-md5sum = f0df1b5cce1edd65addc823da02f9488
x86_64-md5sum = 910c0786459cf1e4dc214e6402d0633e x86_64-md5sum = 910c0786459cf1e4dc214e6402d0633e
[firefox-78] [firefox-102]
<= firefox-download <= firefox-download
version = 78.1.0esr version = 102.15.1esr
i686-md5sum = 09595a1b9a99d17a618a51bc1f971e5e i686-md5sum = 418b51b3553e98070998fcdbc344487d
x86_64-md5sum = 06f4d488721ce7229d9a86cb4c6786f3 x86_64-md5sum = ff477480d34e44fbd0040c32ed905aaf
[firefox-68]
<= firefox-download
version = 68.0.2esr
i686-md5sum = eaa9e0246eb2a31ccf55c100dc2edd5a
x86_64-md5sum = d22dc17ce0949cdff78009afca6f2043
[firefox-60]
<= firefox-download
version = 60.0.2esr
i686-md5sum = ce7c80716036dfb5c2fb1ca2538556ff
x86_64-md5sum = 6fe25d9a3fcc82670320242c9047d1da
[firefox-download] [firefox-download]
recipe = slapos.recipe.build recipe = slapos.recipe.build
...@@ -199,18 +180,6 @@ version = 0.33.0 ...@@ -199,18 +180,6 @@ version = 0.33.0
i686-md5sum = c4a9e6c92dc493f25c8d390f1c6fb11c i686-md5sum = c4a9e6c92dc493f25c8d390f1c6fb11c
x86_64-md5sum = 563c82cfbb21478450e1c828e3730b10 x86_64-md5sum = 563c82cfbb21478450e1c828e3730b10
[geckodriver-0.24.0]
<= geckodriver-base
version = 0.24.0
i686-md5sum = b88eee754f6c90b01f760f7a453dda95
x86_64-md5sum = 7552b85e43973c84763e212af7cca566
[geckodriver-0.22.0]
<= geckodriver-base
version = 0.22.0
i686-md5sum = 6de7544753fda56fbaa8382dcac99aaa
x86_64-md5sum = 81746200ce5841e00cabf3b8ea7db542
[geckodriver-base] [geckodriver-base]
# Installs geckodriver ${version} # Installs geckodriver ${version}
recipe = slapos.recipe.build recipe = slapos.recipe.build
......
...@@ -3,10 +3,11 @@ extends = ...@@ -3,10 +3,11 @@ extends =
../freetype/buildout.cfg ../freetype/buildout.cfg
../libxml2/buildout.cfg ../libxml2/buildout.cfg
../pkgconfig/buildout.cfg ../pkgconfig/buildout.cfg
../python3/buildout.cfg
../bzip2/buildout.cfg ../bzip2/buildout.cfg
../zlib/buildout.cfg ../zlib/buildout.cfg
../bzip2/buildout.cfg
../gperf/buildout.cfg ../gperf/buildout.cfg
../xz-utils/buildout.cfg
buildout.hash.cfg buildout.hash.cfg
parts = parts =
...@@ -15,8 +16,8 @@ parts = ...@@ -15,8 +16,8 @@ parts =
[fontconfig] [fontconfig]
recipe = slapos.recipe.cmmi recipe = slapos.recipe.cmmi
shared = true shared = true
url = http://fontconfig.org/release/fontconfig-2.12.6.tar.bz2 url = https://www.freedesktop.org/software/fontconfig/release/fontconfig-2.14.2.tar.xz
md5sum = 733f5e2371ca77b69707bd7b30cc2163 md5sum = 95261910ea727b5dd116b06fbfd84b1f
pkg_config_depends = ${freetype:pkg_config_depends}:${freetype:location}/lib/pkgconfig:${libxml2:location}/lib/pkgconfig pkg_config_depends = ${freetype:pkg_config_depends}:${freetype:location}/lib/pkgconfig:${libxml2:location}/lib/pkgconfig
configure-options = configure-options =
--disable-static --disable-static
...@@ -24,7 +25,8 @@ configure-options = ...@@ -24,7 +25,8 @@ configure-options =
--enable-libxml2 --enable-libxml2
--with-add-fonts=no --with-add-fonts=no
environment = environment =
PATH=${pkgconfig:location}/bin:${gperf:location}/bin:%(PATH)s PATH=${python3:location}/bin:${pkgconfig:location}/bin:${gperf:location}/bin:${xz-utils:location}/bin:%(PATH)s
PYTHON=${python3:location}/bin/python3
PKG_CONFIG_PATH=${:pkg_config_depends} PKG_CONFIG_PATH=${:pkg_config_depends}
CPPFLAGS=-I${zlib:location}/include -I${bzip2:location}/include CPPFLAGS=-I${zlib:location}/include -I${bzip2:location}/include
LDFLAGS=-L${zlib:location}/lib -Wl,-rpath=${zlib:location}/lib -L${bzip2:location}/lib -Wl,-rpath=${bzip2:location}/lib LDFLAGS=-L${zlib:location}/lib -Wl,-rpath=${zlib:location}/lib -L${bzip2:location}/lib -Wl,-rpath=${bzip2:location}/lib
......
...@@ -13,8 +13,8 @@ parts = gdb ...@@ -13,8 +13,8 @@ parts = gdb
[gdb] [gdb]
recipe = slapos.recipe.cmmi recipe = slapos.recipe.cmmi
shared = true shared = true
url = http://ftp.gnu.org/gnu/gdb/gdb-9.2.tar.xz url = https://ftp.gnu.org/gnu/gdb/gdb-14.1.tar.xz
md5sum = db95524e554870209ab7d9f8fd8dc557 md5sum = 4a084d03915b271f67e9b8ea2ab24972
location = @@LOCATION@@ location = @@LOCATION@@
# gdb refuses to build in-tree -> build it inside build/ # gdb refuses to build in-tree -> build it inside build/
pre-configure = pre-configure =
......
...@@ -30,24 +30,28 @@ environment = ...@@ -30,24 +30,28 @@ environment =
PATH=${swig:location}/bin:${patch:location}/bin:%(PATH)s PATH=${swig:location}/bin:${patch:location}/bin:%(PATH)s
GOROOT_FINAL=${:location} GOROOT_FINAL=${:location}
${:environment-extra} ${:environment-extra}
patch-options = -p1
[golang-common-pre-1.21]
<= golang-common
# TestChown currently fails in a user-namespace # TestChown currently fails in a user-namespace
# https://github.com/golang/go/issues/42525 # https://github.com/golang/go/issues/42525
# the patches apply to go >= 1.12 # the patches apply to 1.21 > go >= 1.12
patch-options = -p1 # (in go 1.21 we can't apply it, due to code changes,
patches = # in go > 1.21 it's hopefully fixed with
# https://github.com/golang/go/commit/9f03e8367d85d75675b2f2e90873e3293799d8aa)
patches +=
${:_profile_base_location_}/skip-chown-tests.patch#d4e3c8ef83788fb2a5d80dd75034786f ${:_profile_base_location_}/skip-chown-tests.patch#d4e3c8ef83788fb2a5d80dd75034786f
[golang-common-pre-1.19] [golang-common-pre-1.19]
<= golang-common <= golang-common-pre-1.21
# TestSCMCredentials fails in a user-namespace if golang version < 1.19 # TestSCMCredentials fails in a user-namespace if golang version < 1.19
# https://github.com/golang/go/issues/42525 # https://github.com/golang/go/issues/42525
patches += patches +=
${:_profile_base_location_}/fix-TestSCMCredentials.patch#1d8dbc97cd579e03fafd8627d48f1c59 ${:_profile_base_location_}/fix-TestSCMCredentials.patch#1d8dbc97cd579e03fafd8627d48f1c59
[golang14] [golang14]
<= golang-common-pre-1.19 <= golang-common-pre-1.19
# https://golang.org/doc/install/source#bootstrapFromSource # https://golang.org/doc/install/source#bootstrapFromSource
...@@ -117,13 +121,28 @@ environment-extra = ...@@ -117,13 +121,28 @@ environment-extra =
GOROOT_BOOTSTRAP=${golang14:location} GOROOT_BOOTSTRAP=${golang14:location}
[golang1.20] [golang1.20]
<= golang-common <= golang-common-pre-1.21
url = https://go.dev/dl/go1.20.6.src.tar.gz url = https://go.dev/dl/go1.20.6.src.tar.gz
md5sum = 1dc2d18790cfaede7df1e73a1eff8b7b md5sum = 1dc2d18790cfaede7df1e73a1eff8b7b
# go1.20 requires go1.17.13 to bootstrap (see https://go.dev/doc/go1.20#bootstrap) # go1.20 requires go1.17.13 to bootstrap (see https://go.dev/doc/go1.20#bootstrap)
environment-extra = environment-extra =
GOROOT_BOOTSTRAP=${golang1.17:location} GOROOT_BOOTSTRAP=${golang1.17:location}
[golang1.21]
<= golang-common
url = https://go.dev/dl/go1.21.5.src.tar.gz
md5sum = 99385ded31906f1554c27015bbbee52d
# go1.21 requires go1.17.13 to bootstrap (see https://go.dev/blog/rebuild)
environment-extra =
GOROOT_BOOTSTRAP=${golang1.17:location}
patches +=
# TestChown fix, old fix doesn't work due to code change,
# for golang > 1.21 this patch is hopefully already included with
# https://github.com/golang/go/commit/9f03e8367d85d75675b2f2e90873e3293799d8aa
${:_profile_base_location_}/os-skip-Chown-tests-for-auxiliary-groups-that-fail-d.patch#81b7f75786d9024049c26d1663b79ba4
${:_profile_base_location_}/skip-unshare-mount-test.patch#325446d5135452e8685e95ab99c13a51
# ---- infrastructure to build Go workspaces / projects ---- # ---- infrastructure to build Go workspaces / projects ----
# gowork is the top-level section that defines Go workspace. # gowork is the top-level section that defines Go workspace.
...@@ -183,7 +202,7 @@ bin = ${gowork.dir:bin} ...@@ -183,7 +202,7 @@ bin = ${gowork.dir:bin}
depends = ${gowork.goinstall:recipe} depends = ${gowork.goinstall:recipe}
# go version used for the workspace (possible to override in applications) # go version used for the workspace (possible to override in applications)
golang = ${golang1.20:location} golang = ${golang1.21:location}
# no special build flags by default # no special build flags by default
buildflags = buildflags =
......
From 9f03e8367d85d75675b2f2e90873e3293799d8aa Mon Sep 17 00:00:00 2001
From: "Bryan C. Mills" <bcmills@google.com>
Date: Tue, 15 Aug 2023 18:01:16 -0400
Subject: [PATCH] os: skip Chown tests for auxiliary groups that fail due to
permission errors
This addresses the failure mode described in
https://git.alpinelinux.org/aports/commit/community/go/tests-filter-overflow-gid.patch?id=9851dde0f5d2a5a50f7f3b5323d1b2ff22e1d028,
but without special-casing an implementation-specific group ID.
For #62053.
Change-Id: I70b1046837b8146889fff7085497213349cd2bf0
Reviewed-on: https://go-review.googlesource.com/c/go/+/520055
Reviewed-by: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Bryan Mills <bcmills@google.com>
Auto-Submit: Bryan Mills <bcmills@google.com>
---
src/os/os_unix_test.go | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/src/os/os_unix_test.go b/src/os/os_unix_test.go
index 9041b25471..e4271ff905 100644
--- a/src/os/os_unix_test.go
+++ b/src/os/os_unix_test.go
@@ -75,6 +75,12 @@ func TestChown(t *testing.T) {
t.Log("groups: ", groups)
for _, g := range groups {
if err = Chown(f.Name(), -1, g); err != nil {
+ if testenv.SyscallIsNotSupported(err) {
+ t.Logf("chown %s -1 %d: %s (error ignored)", f.Name(), g, err)
+ // Since the Chown call failed, the file should be unmodified.
+ checkUidGid(t, f.Name(), int(sys.Uid), gid)
+ continue
+ }
t.Fatalf("chown %s -1 %d: %s", f.Name(), g, err)
}
checkUidGid(t, f.Name(), int(sys.Uid), g)
@@ -123,6 +129,12 @@ func TestFileChown(t *testing.T) {
t.Log("groups: ", groups)
for _, g := range groups {
if err = f.Chown(-1, g); err != nil {
+ if testenv.SyscallIsNotSupported(err) {
+ t.Logf("chown %s -1 %d: %s (error ignored)", f.Name(), g, err)
+ // Since the Chown call failed, the file should be unmodified.
+ checkUidGid(t, f.Name(), int(sys.Uid), gid)
+ continue
+ }
t.Fatalf("fchown %s -1 %d: %s", f.Name(), g, err)
}
checkUidGid(t, f.Name(), int(sys.Uid), g)
@@ -181,12 +193,22 @@ func TestLchown(t *testing.T) {
t.Log("groups: ", groups)
for _, g := range groups {
if err = Lchown(linkname, -1, g); err != nil {
+ if testenv.SyscallIsNotSupported(err) {
+ t.Logf("lchown %s -1 %d: %s (error ignored)", f.Name(), g, err)
+ // Since the Lchown call failed, the file should be unmodified.
+ checkUidGid(t, f.Name(), int(sys.Uid), gid)
+ continue
+ }
t.Fatalf("lchown %s -1 %d: %s", linkname, g, err)
}
checkUidGid(t, linkname, int(sys.Uid), g)
// Check that link target's gid is unchanged.
checkUidGid(t, f.Name(), int(sys.Uid), int(sys.Gid))
+
+ if err = Lchown(linkname, -1, gid); err != nil {
+ t.Fatalf("lchown %s -1 %d: %s", f.Name(), gid, err)
+ }
}
}
--
2.36.0
https://github.com/golang/go/commit/092671423cd95eaa6df93eb29442fef41504d097 breaks
TestUnshareMountNameSpace test on SlapOS.
---
diff --git a/src/syscall/exec_linux_test.go b/src/syscall/exec_linux_test.go
index f4ff7bf81b..bc8bdb0a35 100644
--- a/src/syscall/exec_linux_test.go
+++ b/src/syscall/exec_linux_test.go
@@ -206,6 +206,7 @@ func TestGroupCleanupUserNamespace(t *testing.T) {
// Test for https://go.dev/issue/19661: unshare fails because systemd
// has forced / to be shared
func TestUnshareMountNameSpace(t *testing.T) {
+ t.Skip("skipping: not supported in SlapOS")
testenv.MustHaveExec(t)
if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
...@@ -13,8 +13,8 @@ parts = haproxy ...@@ -13,8 +13,8 @@ parts = haproxy
[haproxy] [haproxy]
recipe = slapos.recipe.cmmi recipe = slapos.recipe.cmmi
shared = true shared = true
url = https://www.haproxy.org/download/2.6/src/haproxy-2.6.15.tar.gz url = https://www.haproxy.org/download/2.6/src/haproxy-2.6.16.tar.gz
md5sum = ecac9724e3ca6368624fb2fab29dd366 md5sum = b01e605cdaf2742fcedf214a61e187b4
configure-command = true configure-command = true
# for Linux kernel 2.6.28 and above, we use "linux-glibc" as the TARGET, # for Linux kernel 2.6.28 and above, we use "linux-glibc" as the TARGET,
# otherwise use "generic". # otherwise use "generic".
...@@ -47,6 +47,7 @@ make-options = ...@@ -47,6 +47,7 @@ make-options =
${:QUIC} ${:QUIC}
USE_PCRE=1 USE_PCRE=1
USE_ZLIB=1 USE_ZLIB=1
USE_PROMEX=1
ZLIB_INC=${zlib:location}/include ZLIB_INC=${zlib:location}/include
ZLIB_LIB=${zlib:location}/lib ZLIB_LIB=${zlib:location}/lib
ADDLIB="${:SSL_ADDLIB} -Wl,-rpath=${pcre:location}/lib -Wl,-rpath=${zlib:location}/lib" ADDLIB="${:SSL_ADDLIB} -Wl,-rpath=${pcre:location}/lib -Wl,-rpath=${zlib:location}/lib"
......
...@@ -5,7 +5,7 @@ parts = ...@@ -5,7 +5,7 @@ parts =
[libsodium] [libsodium]
recipe = slapos.recipe.cmmi recipe = slapos.recipe.cmmi
shared = true shared = true
url = https://download.libsodium.org/libsodium/releases/old/unsupported/libsodium-1.0.8.tar.gz url = https://download.libsodium.org/libsodium/releases/old/libsodium-1.0.17.tar.gz
md5sum = 0a66b86fd3aab3fe4c858edcd2772760 md5sum = 0f71e2680187a1558b5461e6879342c5
configure-options = configure-options =
--disable-static --disable-static
...@@ -72,14 +72,6 @@ md5sum = 28bf6a4d98b238403fa58a0805f4a979 ...@@ -72,14 +72,6 @@ md5sum = 28bf6a4d98b238403fa58a0805f4a979
PATH = ${pkgconfig:location}/bin:${python2.7:location}/bin:%(PATH)s PATH = ${pkgconfig:location}/bin:${python2.7:location}/bin:%(PATH)s
configure-command = ./configure configure-command = ./configure
[nodejs-8.9.4]
<= nodejs-base
version = v8.9.4
md5sum = 4ddc1daff327d7e6f63da57fdfc24f55
openssl-location = ${openssl-1.0:location}
PATH = ${pkgconfig:location}/bin:${python2.7:location}/bin:%(PATH)s
configure-command = ./configure
[nodejs-8.12.0] [nodejs-8.12.0]
<= nodejs-base <= nodejs-base
version = v8.12.0 version = v8.12.0
......
[buildout] [buildout]
extends = extends =
../icu/buildout.cfg
../openssl/buildout.cfg ../openssl/buildout.cfg
../pkgconfig/buildout.cfg
../readline/buildout.cfg ../readline/buildout.cfg
../zlib/buildout.cfg ../zlib/buildout.cfg
../ncurses/buildout.cfg ../ncurses/buildout.cfg
...@@ -30,10 +32,13 @@ configure-options = ...@@ -30,10 +32,13 @@ configure-options =
--without-libxslt --without-libxslt
# build core PostgreSQL + pg_trgm contrib extension for GitLab # build core PostgreSQL + pg_trgm contrib extension for GitLab
# unaccent contrib extension is for peertube # unaccent contrib extension is for peertube
make-targets = install && make -C contrib/pg_trgm/ install && make -C contrib/unaccent/ install # citext contrib extension is for metabase
make-targets = install && make -C contrib/pg_trgm/ install && make -C contrib/unaccent/ install && make -C contrib/citext/ install
environment = environment =
CPPFLAGS=-I${zlib:location}/include -I${readline:location}/include -I${openssl:location}/include -I${ncurses:location}/lib PATH=${pkgconfig:location}/bin:%(PATH)s
LDFLAGS=-L${zlib:location}/lib -Wl,-rpath=${zlib:location}/lib -L${readline:location}/lib -Wl,-rpath=${readline:location}/lib -L${openssl:location}/lib -Wl,-rpath=${openssl:location}/lib -L${ncurses:location}/lib -Wl,-rpath=${ncurses:location}/lib -L${perl:location}/libs-c -Wl,-rpath=${perl:location}/libs-c CPPFLAGS=-I${zlib:location}/include -I${readline:location}/include -I${openssl:location}/include -I${ncurses:location}/include
LDFLAGS=-L${zlib:location}/lib -Wl,-rpath=${zlib:location}/lib -L${readline:location}/lib -Wl,-rpath=${readline:location}/lib -L${openssl:location}/lib -Wl,-rpath=${openssl:location}/lib -L${ncurses:location}/lib -Wl,-rpath=${ncurses:location}/lib -L${perl:location}/libs-c -Wl,-rpath=${perl:location}/libs-c -Wl,-rpath=${icu:location}/lib
PKG_CONFIG_PATH=${icu:location}/lib/pkgconfig/
[postgresql10] [postgresql10]
<= postgresql-common <= postgresql-common
......
...@@ -18,8 +18,10 @@ parts = ...@@ -18,8 +18,10 @@ parts =
# tune pygolang to install with all and for-tests extras. # tune pygolang to install with all and for-tests extras.
# list all_test-dependent eggs that must come through in-tree recipes.
[pygolang] [pygolang]
egg = pygolang[all_test] egg = pygolang[all_test]
depends += ${numpy:egg}
# bin/python is preinstalled with sys.path to pygolang & friends. # bin/python is preinstalled with sys.path to pygolang & friends.
[pygolang-python] [pygolang-python]
......
...@@ -31,7 +31,7 @@ PKG_CONFIG_PATH=${libxml2:location}/lib/pkgconfig:${libxslt:location}/lib/pkgcon ...@@ -31,7 +31,7 @@ PKG_CONFIG_PATH=${libxml2:location}/lib/pkgconfig:${libxslt:location}/lib/pkgcon
[versions] [versions]
xmlsec = 1.3.13 xmlsec = 1.3.13
setuptools-scm = 7.0.5 setuptools-scm = 7.0.5:whl
toml = 0.10.2 toml = 0.10.2
[versions:python2] [versions:python2]
......
...@@ -109,8 +109,8 @@ md5sum = b33775a9ab6eae784b6da9f31be48be3 ...@@ -109,8 +109,8 @@ md5sum = b33775a9ab6eae784b6da9f31be48be3
[debian-amd64-bookworm-netinst.iso] [debian-amd64-bookworm-netinst.iso]
<= debian-amd64-netinst-base <= debian-amd64-netinst-base
version = 12.1.0 version = 12.4.0
md5sum = 8d77d1b0bcfef29e4d56dc0fbe23de15 md5sum = a03cf771ba9513d908093101a094ac88
alternate-url = https://cdimage.debian.org/cdimage/release/current/${:arch}/iso-cd/${:filename} alternate-url = https://cdimage.debian.org/cdimage/release/current/${:arch}/iso-cd/${:filename}
[debian-amd64-netinst.iso] [debian-amd64-netinst.iso]
......
...@@ -50,7 +50,7 @@ CGO_LDFLAGS += -Wl,-rpath=${zlib:location}/lib ...@@ -50,7 +50,7 @@ CGO_LDFLAGS += -Wl,-rpath=${zlib:location}/lib
recipe = slapos.recipe.build:gitclone recipe = slapos.recipe.build:gitclone
repository = https://lab.nexedi.com/nexedi/wendelin.core.git repository = https://lab.nexedi.com/nexedi/wendelin.core.git
branch = master branch = master
revision = wendelin.core-2.0.alpha3-7-g885b355 revision = wendelin.core-2.0.alpha3-9-gda765ef
# dir is pretty name as top-level recipe # dir is pretty name as top-level recipe
location = ${buildout:parts-directory}/wendelin.core location = ${buildout:parts-directory}/wendelin.core
git-executable = ${git:location}/bin/git git-executable = ${git:location}/bin/git
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
[buildout] [buildout]
extends = extends =
# test*.cfg first extend from neoppod/software<ZODB-flavour>.cfg to use ../../stack/erp5/buildout.cfg
# appropriate ZODB and versions of other components. ../../software/neoppod/software.cfg
../pytest/buildout.cfg ../pytest/buildout.cfg
../scipy/buildout.cfg ../scipy/buildout.cfg
......
# SlapOS software release to test wendelin.core/ZODB4-wc2 on Nexedi testing infrastructure. # SlapOS software release to test wendelin.core/ZODB4-wc2 on Nexedi testing infrastructure.
[buildout] [buildout]
extends = test.cfg extends = test-common.cfg
[ZODB] [ZODB]
major = 4-wc2 major = 4-wc2
# SlapOS software release to test wendelin.core/ZODB5 on Nexedi testing infrastructure. # SlapOS software release to test wendelin.core/ZODB5 on Nexedi testing infrastructure.
[buildout] [buildout]
extends = extends = test-common.cfg
../../stack/erp5/buildout.cfg
../../software/neoppod/software-zodb5.cfg [ZODB]
test-common.cfg major = 5
# ZEO[test] needs ZopeUndo
[versions]
ZopeUndo = 5.0
# SlapOS software release to test wendelin.core on Nexedi testing infrastructure.
[buildout]
extends =
../../stack/erp5/buildout.cfg
../../software/neoppod/software.cfg
test-common.cfg
...@@ -10,8 +10,8 @@ extends = ...@@ -10,8 +10,8 @@ extends =
[xmlsec] [xmlsec]
recipe = slapos.recipe.cmmi recipe = slapos.recipe.cmmi
url = https://www.aleksey.com/xmlsec/download/older-releases/xmlsec1-1.2.34.tar.gz url = https://github.com/lsh123/xmlsec/releases/download/xmlsec-1_2_37/xmlsec1-1.2.37.tar.gz
md5sum = 87b0074e7ae535e061acf8ef64dada1b md5sum = 98dd3c884e2816c25c038a6e8af138fb
shared = true shared = true
configure-options = configure-options =
--disable-crypto-dl --disable-crypto-dl
......
...@@ -2,5 +2,8 @@ ...@@ -2,5 +2,8 @@
[buildout] [buildout]
extends = extends =
test-zodb4.cfg test-common.cfg
test-py2.cfg test-py2.cfg
[ZODB]
major = 4
...@@ -2,5 +2,8 @@ ...@@ -2,5 +2,8 @@
[buildout] [buildout]
extends = extends =
test-zodb4-wc2.cfg test-common.cfg
test-py2.cfg test-py2.cfg
[ZODB]
major = 4-wc2
# SlapOS software release to test zodbtools/ZODB4-wc2-py3 on Nexedi testing infrastructure.
[buildout]
extends = test-common.cfg
[ZODB]
major = 4-wc2
# SlapOS software release to test zodbtools/ZODB4-py3 on Nexedi testing infrastructure.
[buildout]
extends = test-common.cfg
[ZODB]
major = 4
...@@ -28,7 +28,7 @@ def main(): ...@@ -28,7 +28,7 @@ def main():
print(e, file=sys.stderr) print(e, file=sys.stderr)
else: else:
with open(f, 'w') as outfile: with open(f, 'w') as outfile:
json.dump(obj, outfile, sort_keys=False, indent=2, separators=(',', ': ')) json.dump(obj, outfile, ensure_ascii=False, sort_keys=False, indent=2, separators=(',', ': '))
outfile.write('\n') outfile.write('\n')
sys.exit(exit_code) sys.exit(exit_code)
......
...@@ -28,7 +28,7 @@ from setuptools import setup, find_packages ...@@ -28,7 +28,7 @@ from setuptools import setup, find_packages
import glob import glob
import os import os
version = '1.0.329' version = '1.0.351'
name = 'slapos.cookbook' name = 'slapos.cookbook'
long_description = open("README.rst").read() long_description = open("README.rst").read()
...@@ -72,6 +72,8 @@ setup(name=name, ...@@ -72,6 +72,8 @@ setup(name=name,
'zc.buildout', # plays with buildout 'zc.buildout', # plays with buildout
'zc.recipe.egg', # for scripts generation 'zc.recipe.egg', # for scripts generation
'pytz', # for timezone database 'pytz', # for timezone database
'passlib',
'bcrypt',
], ],
zip_safe=True, zip_safe=True,
entry_points={ entry_points={
......
...@@ -45,10 +45,8 @@ class Recipe(GenericBaseRecipe): ...@@ -45,10 +45,8 @@ class Recipe(GenericBaseRecipe):
elif self.options.get('expected-type') == "ipv4": elif self.options.get('expected-type') == "ipv4":
template = self.getTemplateFilename('check_ipv4.py.in') template = self.getTemplateFilename('check_ipv4.py.in')
else: else:
config["expected-value"] = self.options.get('expected-value') config["expected-value"] = str(self.options.get('expected-value', ''))
config["expected-not-value"] = str(self.options.get('expected-not-value', ''))
config["expected-not-value"] = self.options.get('expected-not-value')
template = self.getTemplateFilename('check_parameter.py.in') template = self.getTemplateFilename('check_parameter.py.in')
promise = self.createExecutable( promise = self.createExecutable(
......
...@@ -5,14 +5,14 @@ from __future__ import print_function ...@@ -5,14 +5,14 @@ from __future__ import print_function
import socket import socket
import sys import sys
value = "%(value)s" value = %(value)r
expected = "%(expected-value)s" expected = %(expected-value)r
not_expected = "%(expected-not-value)s" not_expected = %(expected-not-value)r
if expected != "" and value != expected: if expected != "" and value != expected:
print("FAIL: %%s != %%s" %% (value, expected)) print("FAIL: %%s != %%s" %% (value, expected))
sys.exit(127) sys.exit(127)
if not_expected != "" and value == not_expected: if not_expected != "" and value == not_expected:
print("FAIL: %%s == %%s" %% (value, not_expected)) print("FAIL: %%s == %%s" %% (value, not_expected))
sys.exit(127) sys.exit(127)
...@@ -24,9 +24,14 @@ ...@@ -24,9 +24,14 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# #
############################################################################## ##############################################################################
import logging
import os import os
import time
from inotify_simple import INotify, flags from inotify_simple import INotify, flags
logger = logging.getLogger(__name__)
def subfiles(directory): def subfiles(directory):
"""Return the list of subfiles of a directory, and wait for the newly created """Return the list of subfiles of a directory, and wait for the newly created
ones. ones.
...@@ -35,10 +40,19 @@ def subfiles(directory): ...@@ -35,10 +40,19 @@ def subfiles(directory):
ALWAYS ITERATE OVER IT !!!*""" ALWAYS ITERATE OVER IT !!!*"""
with INotify() as inotify: with INotify() as inotify:
inotify.add_watch(directory, flags.CLOSE_WRITE | flags.MOVED_TO) try:
inotify.add_watch(directory, flags.CLOSE_WRITE | flags.MOVED_TO)
inotify_available = True
except OSError:
logger.warning("Unable to add inotify watch, falling back to polling")
inotify_available = False
names = os.listdir(directory) names = os.listdir(directory)
while True: while True:
for name in names: for name in names:
yield os.path.join(directory, name) yield os.path.join(directory, name)
names = (event.name for event in inotify.read()) if inotify_available:
names = (event.name for event in inotify.read())
else:
time.sleep(5)
names = os.listdir(directory)
...@@ -131,7 +131,9 @@ class Recipe(GenericSlapRecipe): ...@@ -131,7 +131,9 @@ class Recipe(GenericSlapRecipe):
new = {} new = {}
for k, v in six.iteritems(init): for k, v in six.iteritems(init):
try: try:
options[k] = publish_dict[k] = new[v] = init_section.pop(v) init_section_value = init_section[v]
options[k] = publish_dict[k] = new[v] = init_section_value
del init_section[v]
except KeyError: except KeyError:
pass pass
if new != override: if new != override:
......
...@@ -33,12 +33,16 @@ buildout Software Releases and Instances developments. ...@@ -33,12 +33,16 @@ buildout Software Releases and Instances developments.
from __future__ import absolute_import from __future__ import absolute_import
import errno import errno
import json
import os import os
import random import random
import string import string
import sys
from .librecipe import GenericBaseRecipe from .librecipe import GenericBaseRecipe
from .publish_early import volatileOptions from .publish_early import volatileOptions
from slapos.util import str2bytes
import passlib.hash
class Integer(object): class Integer(object):
""" """
...@@ -113,7 +117,7 @@ def generatePassword(length): ...@@ -113,7 +117,7 @@ def generatePassword(length):
class Password(object): class Password(object):
"""Generate a password that is only composed of lowercase letters """Generate a password.
This recipe only makes sure that ${:passwd} does not end up in `.installed` This recipe only makes sure that ${:passwd} does not end up in `.installed`
file, which is world-readable by default. So be careful not to spread it file, which is world-readable by default. So be careful not to spread it
...@@ -128,6 +132,11 @@ class Password(object): ...@@ -128,6 +132,11 @@ class Password(object):
- create-once: boolean value which set if storage-path won't be modified - create-once: boolean value which set if storage-path won't be modified
as soon the file is created with the password (not empty). as soon the file is created with the password (not empty).
(default: True) (default: True)
- passwd: the generated password. Can also be set, to reuse the password
hashing capabilities.
- passwd-*: the hashed password, using schemes supported by passlib.
for example, passwd-sha256-crypt will expose the password hashed
with sha256 crypt algorithm.
If storage-path is empty, the recipe does not save the password, which is If storage-path is empty, the recipe does not save the password, which is
fine it is saved by other means, e.g. using the publish-early recipe. fine it is saved by other means, e.g. using the publish-early recipe.
...@@ -141,24 +150,53 @@ class Password(object): ...@@ -141,24 +150,53 @@ class Password(object):
except KeyError: except KeyError:
self.storage_path = options['storage-path'] = os.path.join( self.storage_path = options['storage-path'] = os.path.join(
buildout['buildout']['parts-directory'], name) buildout['buildout']['parts-directory'], name)
passwd = options.get('passwd') passwd_dict = {
if not passwd: '': options.get('passwd')
}
if not passwd_dict['']:
if self.storage_path: if self.storage_path:
self._needs_migration = False
try: try:
with open(self.storage_path) as f: with open(self.storage_path) as f:
passwd = f.read().strip('\n') content = f.read().strip('\n')
# new format: the file contains password and hashes in json format
try:
passwd_dict = json.loads(content)
if sys.version_info < (3, ):
passwd_dict = {k: v.encode() for k, v in passwd_dict.items()}
except ValueError:
# old format: the file only contains the password in plain text
passwd_dict[''] = content
self._needs_migration = True
except IOError as e: except IOError as e:
if e.errno != errno.ENOENT: if e.errno != errno.ENOENT:
raise raise
if not passwd:
passwd = self.generatePassword(int(options.get('bytes', '16'))) if not passwd_dict['']:
passwd_dict[''] = self.generatePassword(int(options.get('bytes', '16')))
self.update = self.install self.update = self.install
options['passwd'] = passwd options['passwd'] = passwd_dict['']
class HashedPasswordDict(dict):
def __missing__(self, key):
if not key.startswith('passwd-'):
raise KeyError(key)
if key in passwd_dict:
return passwd_dict[key]
handler = getattr(
passlib.hash, key[len('passwd-'):].replace('-', '_'), None)
if handler is None:
raise KeyError(key)
hashed = handler.hash(passwd_dict[''])
passwd_dict[key] = hashed
return hashed
options._data = HashedPasswordDict(options._data)
# Password must not go into .installed file, for 2 reasons: # Password must not go into .installed file, for 2 reasons:
# security of course but also to prevent buildout to always reinstall. # security of course but also to prevent buildout to always reinstall.
# publish_early already does it, but this recipe may also be used alone. # publish_early already does it, but this recipe may also be used alone.
volatileOptions(options, ('passwd',)) volatileOptions(options, ('passwd',))
self.passwd = passwd self.passwd_dict = passwd_dict
generatePassword = staticmethod(generatePassword) generatePassword = staticmethod(generatePassword)
...@@ -167,19 +205,14 @@ class Password(object): ...@@ -167,19 +205,14 @@ class Password(object):
try: try:
# The following 2 lines are just an optimization to avoid recreating # The following 2 lines are just an optimization to avoid recreating
# the file with the same content. # the file with the same content.
if self.create_once and os.stat(self.storage_path).st_size: if self.create_once and os.stat(self.storage_path).st_size and not self._needs_migration:
return return
os.unlink(self.storage_path) os.unlink(self.storage_path)
except OSError as e: except OSError as e:
if e.errno != errno.ENOENT: if e.errno != errno.ENOENT:
raise raise
with open(self.storage_path, 'w') as f:
fd = os.open(self.storage_path, json.dump(self.passwd_dict, f)
os.O_CREAT | os.O_EXCL | os.O_WRONLY | os.O_TRUNC, 0o600)
try:
os.write(fd, str2bytes(self.passwd))
finally:
os.close(fd)
if not self.create_once: if not self.create_once:
return self.storage_path return self.storage_path
......
import os
import subprocess
import tempfile
import unittest
import zc.buildout.testing
from slapos.recipe import check_parameter
class TestCheckParameter(unittest.TestCase):
def setUp(self):
self.buildout = zc.buildout.testing.Buildout()
def _makeRecipe(self, options):
path = tempfile.NamedTemporaryFile(delete=False).name
self.addCleanup(os.unlink, path)
options.setdefault("path", path)
self.buildout["check-parameter"] = options
recipe = check_parameter.Recipe(
self.buildout, "check-parameter", self.buildout["check-parameter"]
)
return recipe
def test_expected_value_ok(self):
script = self._makeRecipe({"expected-value": "foo", "value": "foo"}).install()
subprocess.check_call(script)
def test_expected_value_not_ok(self):
script = self._makeRecipe({"expected-value": "foo", "value": "bar"}).install()
with self.assertRaises(subprocess.CalledProcessError) as e:
subprocess.check_output(script, universal_newlines=True)
self.assertEqual(e.exception.output, "FAIL: bar != foo\n")
def test_expected_value_multi_lines_ok(self):
script = self._makeRecipe(
{"expected-value": "foo\nbar", "value": "foo\nbar"}
).install()
subprocess.check_output(script)
def test_expected_value_multi_lines_not_ok(self):
script = self._makeRecipe({"expected-value": "foo\nbar", "value": "foo"}).install()
with self.assertRaises(subprocess.CalledProcessError) as e:
subprocess.check_output(script, universal_newlines=True)
self.assertEqual(e.exception.output, "FAIL: foo != foo\nbar\n")
def test_expected_not_value_ok(self):
script = self._makeRecipe({"expected-not-value": "foo", "value": "bar"}).install()
subprocess.check_call(script)
def test_expected_not_value_not_ok(self):
script = self._makeRecipe({"expected-not-value": "foo", "value": "foo"}).install()
with self.assertRaises(subprocess.CalledProcessError) as e:
subprocess.check_output(script, universal_newlines=True)
self.assertEqual(e.exception.output, "FAIL: foo == foo\n")
import json
import os
import shutil
import tempfile
import unittest
import zc.buildout.testing
import zc.buildout.buildout
import passlib.hash
from slapos.recipe import random
class TestPassword(unittest.TestCase):
def setUp(self):
self.buildout = zc.buildout.testing.Buildout()
parts_directory = tempfile.mkdtemp()
self.buildout['buildout']['parts-directory'] = parts_directory
self.addCleanup(shutil.rmtree, parts_directory)
def _makeRecipe(self, options, section_name="random"):
self.buildout[section_name] = options
recipe = random.Password(
self.buildout, section_name, self.buildout[section_name]
)
return recipe
def test_empty_options(self):
recipe = self._makeRecipe({})
passwd = self.buildout["random"]["passwd"]
self.assertEqual(len(passwd), 16)
recipe.install()
with open(self.buildout["random"]["storage-path"]) as f:
self.assertEqual(json.load(f), {'': passwd})
def test_storage_path(self):
tf = tempfile.NamedTemporaryFile(delete=False)
self.addCleanup(os.unlink, tf.name)
self._makeRecipe({'storage-path': tf.name}).install()
passwd = self.buildout["random"]["passwd"]
self.assertEqual(len(passwd), 16)
with open(tf.name) as f:
self.assertEqual(json.load(f), {'': passwd})
self._makeRecipe({'storage-path': tf.name}, "another").install()
self.assertEqual(self.buildout["another"]["passwd"], passwd)
def test_storage_path_legacy_format(self):
with tempfile.NamedTemporaryFile(delete=False) as tf:
tf.write(b'secret\n')
tf.flush()
self._makeRecipe({'storage-path': tf.name}).install()
passwd = self.buildout["random"]["passwd"]
self.assertEqual(passwd, 'secret')
tf.flush()
with open(tf.name) as f:
self.assertEqual(json.load(f), {'': 'secret'})
self._makeRecipe({'storage-path': tf.name}, "another").install()
self.assertEqual(self.buildout["another"]["passwd"], passwd)
def test_bytes(self):
self._makeRecipe({'bytes': '32'}).install()
passwd = self.buildout["random"]["passwd"]
self.assertEqual(len(passwd), 32)
with open(self.buildout["random"]["storage-path"]) as f:
self.assertEqual(json.load(f), {'': passwd})
def test_volatile(self):
self._makeRecipe({})
options = self.buildout['random']
self.assertIn('passwd', options)
options_items = [(k, v) for k, v in options.items() if k != 'passwd']
copied_options = options.copy()
self.assertEqual(list(copied_options.items()), options_items)
def test_passlib(self):
recipe = self._makeRecipe({})
hashed = self.buildout['random']['passwd-sha256-crypt']
self.assertTrue(
passlib.hash.sha256_crypt.verify(
self.buildout['random']['passwd'], hashed))
hashed = self.buildout['random']['passwd-md5-crypt']
self.assertTrue(
passlib.hash.md5_crypt.verify(
self.buildout['random']['passwd'], hashed))
hashed = self.buildout['random']['passwd-bcrypt']
self.assertTrue(
passlib.hash.bcrypt.verify(
self.buildout['random']['passwd'], hashed))
hashed = self.buildout['random']['passwd-ldap-salted-sha1']
self.assertTrue(
passlib.hash.ldap_salted_sha1.verify(
self.buildout['random']['passwd'], hashed))
with self.assertRaises(zc.buildout.buildout.MissingOption):
self.buildout['random']['passwd-unknown']
with self.assertRaises(zc.buildout.buildout.MissingOption):
self.buildout['random']['unknown']
copied_options = self.buildout['random'].copy()
self.assertEqual(list(copied_options.keys()), ['storage-path'])
recipe.install()
# when buildout runs again, the values are read from the storage
# and even the hashed values are the same
self._makeRecipe({'storage-path': self.buildout['random']['storage-path']}, 'reread')
self.assertEqual(
self.buildout['reread']['passwd'],
self.buildout['random']['passwd'])
self.assertEqual(
self.buildout['reread']['passwd-sha256-crypt'],
self.buildout['random']['passwd-sha256-crypt'])
self.assertEqual(
self.buildout['reread']['passwd-bcrypt'],
self.buildout['random']['passwd-bcrypt'])
self.assertEqual(
self.buildout['reread']['passwd-ldap-salted-sha1'],
self.buildout['random']['passwd-ldap-salted-sha1'])
# values are strings which is important for python2
self.assertIsInstance(self.buildout['reread']['passwd'], str)
self.assertIsInstance(self.buildout['reread']['passwd-ldap-salted-sha1'], str)
def test_passlib_input_passwd(self):
self._makeRecipe({'passwd': 'insecure'})
self.assertEqual(self.buildout['random']['passwd'], 'insecure')
hashed = self.buildout['random']['passwd-sha256-crypt']
self.assertTrue(passlib.hash.sha256_crypt.verify('insecure', hashed))
...@@ -85,6 +85,7 @@ def createFormatTest(path): ...@@ -85,6 +85,7 @@ def createFormatTest(path):
self.assertEqual( self.assertEqual(
(json.dumps( (json.dumps(
json.loads(content, object_pairs_hook=collections.OrderedDict), json.loads(content, object_pairs_hook=collections.OrderedDict),
ensure_ascii=False,
sort_keys=False, sort_keys=False,
indent=2, indent=2,
separators=(',', ': ')) + "\n").splitlines(), separators=(',', ': ')) + "\n").splitlines(),
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
}, },
"mimetype-entry-addition": { "mimetype-entry-addition": {
"description": "The list of entry to add to the cloudooo mimetype registry. Each entry should on one line which format is: \"<source_mimetype> <destination_mimetype> <handler>\"", "description": "The list of entry to add to the cloudooo mimetype registry. Each entry should on one line which format is: \"<source_mimetype> <destination_mimetype> <handler>\"",
"textarea": true,
"type": "string" "type": "string"
} }
} }
......
...@@ -37,9 +37,12 @@ import base64 ...@@ -37,9 +37,12 @@ import base64
import io import io
import requests import requests
import PIL.Image
import PyPDF2 import PyPDF2
from slapos.testing.testcase import makeModuleSetUpAndTestCaseClass from slapos.testing.testcase import makeModuleSetUpAndTestCaseClass
from slapos.testing.utils import ImageComparisonTestCase
setUpModule, _CloudOooTestCase = makeModuleSetUpAndTestCaseClass( setUpModule, _CloudOooTestCase = makeModuleSetUpAndTestCaseClass(
os.path.abspath( os.path.abspath(
...@@ -148,7 +151,6 @@ class TestWkhtmlToPDF(HTMLtoPDFConversionFontTestMixin, CloudOooTestCase): ...@@ -148,7 +151,6 @@ class TestWkhtmlToPDF(HTMLtoPDFConversionFontTestMixin, CloudOooTestCase):
'Courier New': 'LiberationSans', 'Courier New': 'LiberationSans',
'DejaVu Sans': 'DejaVuSans', 'DejaVu Sans': 'DejaVuSans',
'DejaVu Sans Condensed': 'LiberationSans', 'DejaVu Sans Condensed': 'LiberationSans',
'DejaVu Sans ExtraLight': 'LiberationSans',
'DejaVu Sans Mono': 'DejaVuSansMono', 'DejaVu Sans Mono': 'DejaVuSansMono',
'DejaVu Serif': 'DejaVuSerif', 'DejaVu Serif': 'DejaVuSerif',
'DejaVu Serif Condensed': 'LiberationSans', 'DejaVu Serif Condensed': 'LiberationSans',
...@@ -165,11 +167,11 @@ class TestWkhtmlToPDF(HTMLtoPDFConversionFontTestMixin, CloudOooTestCase): ...@@ -165,11 +167,11 @@ class TestWkhtmlToPDF(HTMLtoPDFConversionFontTestMixin, CloudOooTestCase):
'Liberation Sans Narrow': 'LiberationSansNarrow', 'Liberation Sans Narrow': 'LiberationSansNarrow',
'Liberation Serif': 'LiberationSerif', 'Liberation Serif': 'LiberationSerif',
'Linux LibertineG': 'LiberationSans', 'Linux LibertineG': 'LiberationSans',
'OpenSymbol': {'DejaVuSans', 'OpenSymbol'}, 'OpenSymbol': {'NotoSans-Regular', 'OpenSymbol'},
'Palatino': 'LiberationSans', 'Palatino': 'LiberationSans',
'Roboto Black': 'LiberationSans', 'Roboto Black': 'LiberationSans',
'Roboto Condensed Light': 'LiberationSans', 'Roboto Condensed Light': 'LiberationSans',
'Roboto Condensed Regular': 'LiberationSans', 'Roboto Condensed': 'RobotoCondensed-Regular',
'Roboto Light': 'LiberationSans', 'Roboto Light': 'LiberationSans',
'Roboto Medium': 'LiberationSans', 'Roboto Medium': 'LiberationSans',
'Roboto Thin': 'LiberationSans', 'Roboto Thin': 'LiberationSans',
...@@ -198,43 +200,42 @@ class TestLibreoffice(HTMLtoPDFConversionFontTestMixin, CloudOooTestCase): ...@@ -198,43 +200,42 @@ class TestLibreoffice(HTMLtoPDFConversionFontTestMixin, CloudOooTestCase):
pdf_producer = 'LibreOffice 7.5' pdf_producer = 'LibreOffice 7.5'
expected_font_mapping = { expected_font_mapping = {
'Arial': 'LiberationSans', 'Arial': 'LiberationSans',
'Arial Black': 'DejaVuSans', 'Arial Black': 'NotoSans-Regular',
'Avant Garde': 'DejaVuSans', 'Avant Garde': 'NotoSans-Regular',
'Bookman': 'DejaVuSans', 'Bookman': 'NotoSans-Regular',
'Carlito': 'Carlito', 'Carlito': 'Carlito',
'Comic Sans MS': 'DejaVuSans', 'Comic Sans MS': 'NotoSans-Regular',
'Courier New': 'LiberationMono', 'Courier New': 'LiberationMono',
'DejaVu Sans': 'DejaVuSans', 'DejaVu Sans': 'DejaVuSans',
'DejaVu Sans Condensed': 'DejaVuSansCondensed', 'DejaVu Sans Condensed': 'DejaVuSansCondensed',
'DejaVu Sans ExtraLight': 'DejaVuSans',
'DejaVu Sans Mono': 'DejaVuSansMono', 'DejaVu Sans Mono': 'DejaVuSansMono',
'DejaVu Serif': 'DejaVuSerif', 'DejaVu Serif': 'DejaVuSerif',
'DejaVu Serif Condensed': 'DejaVuSerifCondensed', 'DejaVu Serif Condensed': 'DejaVuSerifCondensed',
'Garamond': 'DejaVuSerif', 'Garamond': 'NotoSerif-Regular',
'Gentium Basic': 'GentiumBasic', 'Gentium Basic': 'GentiumBasic',
'Gentium Book Basic': 'GentiumBookBasic', 'Gentium Book Basic': 'GentiumBookBasic',
'Georgia': 'DejaVuSerif', 'Georgia': 'NotoSerif-Regular',
'Helvetica': 'LiberationSans', 'Helvetica': 'LiberationSans',
'IPAex Gothic': 'IPAexGothic', 'IPAex Gothic': 'IPAexGothic',
'IPAex Mincho': 'IPAexMincho', 'IPAex Mincho': 'IPAexMincho',
'Impact': 'DejaVuSans', 'Impact': 'NotoSans-Regular',
'Liberation Mono': 'LiberationMono', 'Liberation Mono': 'LiberationMono',
'Liberation Sans': 'LiberationSans', 'Liberation Sans': 'LiberationSans',
'Liberation Sans Narrow': 'LiberationSansNarrow', 'Liberation Sans Narrow': 'LiberationSansNarrow',
'Liberation Serif': 'LiberationSerif', 'Liberation Serif': 'LiberationSerif',
'Linux LibertineG': 'LinuxLibertineG', 'Linux LibertineG': 'LinuxLibertineG',
'OpenSymbol': {'OpenSymbol', 'IPAMincho'}, 'OpenSymbol': {'OpenSymbol', 'IPAMincho'},
'Palatino': 'DejaVuSerif', 'Palatino': 'NotoSerif-Regular',
'Roboto Black': 'Roboto-Black', 'Roboto Black': 'Roboto-Black',
'Roboto Condensed Light': 'RobotoCondensed-Light', 'Roboto Condensed Light': 'RobotoCondensed-Light',
'Roboto Condensed Regular': 'DejaVuSans', 'Roboto Condensed': 'RobotoCondensed-Regular',
'Roboto Light': 'Roboto-Light', 'Roboto Light': 'Roboto-Light',
'Roboto Medium': 'Roboto-Medium', 'Roboto Medium': 'Roboto-Medium',
'Roboto Thin': 'Roboto-Thin', 'Roboto Thin': 'Roboto-Thin',
'Times New Roman': 'LiberationSerif', 'Times New Roman': 'LiberationSerif',
'Trebuchet MS': 'DejaVuSans', 'Trebuchet MS': 'NotoSans-Regular',
'Verdana': 'DejaVuSans', 'Verdana': 'NotoSans-Regular',
'ZZZdefault fonts when no match': 'DejaVuSans' 'ZZZdefault fonts when no match': 'NotoSans-Regular'
} }
def _convert_html_to_pdf(self, src_html): def _convert_html_to_pdf(self, src_html):
...@@ -246,6 +247,27 @@ class TestLibreoffice(HTMLtoPDFConversionFontTestMixin, CloudOooTestCase): ...@@ -246,6 +247,27 @@ class TestLibreoffice(HTMLtoPDFConversionFontTestMixin, CloudOooTestCase):
).encode()) ).encode())
class TestLibreofficeDrawToPNGConversion(CloudOooTestCase, ImageComparisonTestCase):
__partition_reference__ = 'l'
def test(self):
reference_png = PIL.Image.open(os.path.join('data', f'{self.id()}.png'))
with open(os.path.join('data', f'{self.id()}.odg'), 'rb') as f:
actual_png_data = base64.decodebytes(
self.server.convertFile(
base64.encodebytes(f.read()).decode(),
'odg',
'png',
).encode())
actual_png = PIL.Image.open(io.BytesIO(actual_png_data))
# save a snapshot
with open(os.path.join(self.computer_partition_root_path, self.id() + '.png'), 'wb') as f:
f.write(actual_png_data)
self.assertImagesSame(actual_png, reference_png)
class TestLibreOfficeTextConversion(CloudOooTestCase): class TestLibreOfficeTextConversion(CloudOooTestCase):
__partition_reference__ = 'txt' __partition_reference__ = 'txt'
......
[instance] [instance]
filename = instance.cfg filename = instance.cfg
md5sum = a4e19280bc672cc98e0fef241c8439ba md5sum = e4092f606716171a5c9c5980c70573b3
[buildout] [buildout]
parts = parts =
dream_simulation dream-simulation
dream_platform dream-platform
dream_test_suite dream-test-suite
dream_interpreter dream-interpreter
grunt_watch grunt-watch
publish-connection-parameter publish-connection-parameter
dream-platform-url-available dream-platform-url-available
...@@ -22,44 +22,44 @@ url = $${slap-connection:server-url} ...@@ -22,44 +22,44 @@ url = $${slap-connection:server-url}
key = $${slap-connection:key-file} key = $${slap-connection:key-file}
cert = $${slap-connection:cert-file} cert = $${slap-connection:cert-file}
[dream_platform_parameter] [dream-platform-parameter]
port = 18080 port = 18080
host = $${instance-parameter:ipv6-random} host = $${instance-parameter:ipv6-random}
url = http://[$${:host}]:$${:port} url = http://[$${:host}]:$${:port}
# interpreter # interpreter
[dream_interpreter] [dream-interpreter]
recipe = slapos.cookbook:wrapper recipe = slapos.cookbook:wrapper
command-line = ${buildout:bin-directory}/dream_interpreter command-line = ${buildout:bin-directory}/dream_interpreter
wrapper-path = $${buildout:bin-directory}/dream_interpreter wrapper-path = $${buildout:bin-directory}/dream_interpreter
# service # service
[dream_platform] [dream-platform]
recipe = slapos.cookbook:wrapper recipe = slapos.cookbook:wrapper
command-line = ${buildout:bin-directory}/dream_platform --debug --host $${dream_platform_parameter:host} --port $${dream_platform_parameter:port} --log $${directory:log}/dream_platform.log command-line = ${buildout:bin-directory}/dream_platform --debug --host $${dream-platform-parameter:host} --port $${dream-platform-parameter:port} --log $${directory:log}/dream_platform.log
wrapper-path = $${directory:service}/dream_platform wrapper-path = $${directory:service}/dream_platform
[dream-platform-url-available] [dream-platform-url-available]
<= monitor-promise-base <= monitor-promise-base
promise = check_url_available promise = check_url_available
name = $${:_buildout_section_name_}.py name = $${:_buildout_section_name_}.py
config-url= $${dream_platform_parameter:url} config-url= $${dream-platform-parameter:url}
[grunt_watch] [grunt-watch]
recipe = slapos.cookbook:wrapper recipe = slapos.cookbook:wrapper
command-line = bash -c 'cd ${dream-repository.git:location}; PATH=${nodejs:location}/bin/:$PATH ${dream-repository.git:location}/node_modules/grunt-cli/bin/grunt watch -f > $${directory:log}/grunt.log' command-line = bash -c 'cd ${dream-repository.git:location}; PATH=${nodejs:location}/bin/:$PATH ${dream-repository.git:location}/node_modules/grunt-cli/bin/grunt watch -f > $${directory:log}/grunt.log'
wrapper-path = $${directory:service}/dream_grunt_watch wrapper-path = $${directory:service}/dream_grunt_watch
# CLI # CLI
[dream_simulation] [dream-simulation]
recipe = slapos.cookbook:wrapper recipe = slapos.cookbook:wrapper
command-line = ${buildout:bin-directory}/dream_simulation command-line = ${buildout:bin-directory}/dream_simulation
wrapper-path = $${directory:script}/dream_simulation wrapper-path = $${directory:script}/dream_simulation
[dream_test_suite] [dream-test-suite]
recipe = slapos.cookbook:wrapper recipe = slapos.cookbook:wrapper
command-line = ${dream_testrunner:script} command-line = ${dream-testrunner:script}
wrapper-path = $${directory:script}/dream_test_suite wrapper-path = $${directory:script}/dream_test_suite
[directory] [directory]
...@@ -74,4 +74,4 @@ log = $${:var}/log ...@@ -74,4 +74,4 @@ log = $${:var}/log
[publish-connection-parameter] [publish-connection-parameter]
recipe = slapos.cookbook:publishurl recipe = slapos.cookbook:publishurl
url = $${dream_platform_parameter:url} url = $${dream-platform-parameter:url}
...@@ -9,8 +9,8 @@ extends = ...@@ -9,8 +9,8 @@ extends =
parts = parts =
slapos-cookbook slapos-cookbook
manpy manpy
dream_testrunner dream-testrunner
npm_install npm-install
instance instance
[gcc] [gcc]
...@@ -20,22 +20,22 @@ max_version = 0 ...@@ -20,22 +20,22 @@ max_version = 0
[python] [python]
part = python2.7 part = python2.7
[dream-repository.git]
revision = f3bcf115741886835df8c0ca0fdbf510d77d8db8
[instance] [instance]
recipe = slapos.recipe.template recipe = slapos.recipe.template
url = ${:_profile_base_location_}/${:filename} url = ${:_profile_base_location_}/${:filename}
output = ${buildout:directory}/instance.cfg output = ${buildout:directory}/instance.cfg
[dream_testrunner] [dream-testrunner]
recipe = zc.recipe.testrunner recipe = zc.recipe.testrunner
eggs = dream eggs = dream
script = dream_testrunner script = dream_testrunner
initialization = initialization =
${manpy:initialization} ${manpy:initialization}
[nodejs] [npm-install]
<= nodejs-8.9.4
[npm_install]
recipe = plone.recipe.command recipe = plone.recipe.command
stop-on-error = true stop-on-error = true
command = command =
...@@ -52,8 +52,4 @@ simpy = 3.0.5 ...@@ -52,8 +52,4 @@ simpy = 3.0.5
zope.dottedname = 4.1.0 zope.dottedname = 4.1.0
tablib = 0.10.0 tablib = 0.10.0
mysqlclient = 1.3.12 mysqlclient = 1.3.12
# indirect dependancies
cp.recipe.cmd = 0.5
plone.recipe.command = 1.1
zc.recipe.testrunner = 2.0.0 zc.recipe.testrunner = 2.0.0
# End To End Testing
This software release is used to run end-to-end test of SlapOS softwares on an actual SlapOS cloud such as Rapid.Space. Since it can supply softwares and request instances on an actual cloud, it needs a SlaPOS client certificate and the URLs of your tests.
## Input parameters
```
{
"client.crt": <content of client.crt>,
"client.key": <content of client.key>,
"master-url": <url of SlapOS master>,
"tests": [
{
"url": "<url of test1 script>",
"md5sum": "MD5sum of test1 script"
},
{
"url": "<url of test2 python script>",
"md5sum": "MD5sum of test2 python script"
},
...
]
}
```
Example:
(`e2e-parameters.json`)
```
{
"client.crt": "Certificate:...-----END CERTIFICATE-----\n",
"master-url": "https://slap.vifib.com",
"tests": [
{
"url": "https://lab.nexedi.com/lu.xu/slapos/raw/feat/end-to-end-testing/software/end-to-end-testing/test_test.py",
"md5sum": "c074373dbb4154aa924ef5781dade7a0"
}
]
}
```
## Generate client certificate
Follow [How To Set Up SlapOS Client](https://handbook.rapid.space/user/rapidspace-HowTo.Setup.SlapOS.Client) to prepare `slapos-client.cfg` if you don't have one.
A convenience script `generate_parameters.py` is provided to compute these parameters in JSON format from an existing SlapOS client configuration:
```
python3 generate_parameters.py --cfg <absolute path to slapos-client.cfg> -o <output path>
```
## Adding tests
There are 3 example tests available in end-to-end testing SR:
- test_test.py
Simple successful test and failed test
- test_kvm.py
Request a KVM instance with published SR and verify one of the connection parameters
- test_health.py
Request a Monitor instance with published SR and log promises output
All tests should be written in Python with a `.py` extension and should have names that start with `test_`.
Once your test is prepared, you have the option to input a URL and its corresponding md5sum as parameters. This will enable the end-to-end testing instance to automatically detect the test.
## Running tests
### In Nexedi ERP5 automated test environment (testnodes)
When performing tests on a software release (SR) using the ERR5 test suite, you can use "SlapOS.SoftwareReleases.IntegrationTest". Additionally, make sure to fill in the "Slapos Parameters" field with the content of the input parameter mentioned above.
### In Theia locally
#### Using virtualenv
Using slapos.core for quick testing:
```
python3 -m venv testenv
source testenv/bin/activate
pip install -e path/to/my/slapos.core
export SLAPOS_E2E_TEST_CLIENT_CFG=my_test_client_cfg
export SLAPOS_E2E_TEST_LOG_FILE=my_test_log_file
python -m unittest my_test_file_in_development
# edit e2e.py in path/to/my/slapos.core or the instanciated one if improvements are needed
```
#### Using an instance of software/end-to-end-testing
1. Setup and instantiate the runner
```
slapos supply ~/srv/project/slapos/software/end-to-end-testing/software.cfg slaprunner
slapos request <e2e_instance_name> ~/srv/project/slapos/software/end-to-end-testing/software.cfg --parameters-file <e2e_parameter_json_file>
```
Your tests should be listed in <e2e_parameter_json_file> with URL and MD5sum
2. Go to instance directory and run test
```
cd ~/srv/runner/instance/slappartX
./bin/runTestSuite
```
Downloaded tests and the reusable `e2e.py` script can be found in the `~/srv/runner/instance/slappartX/var/tests/` directory.
To quickly test, you have the option to modify the test script directly here(`~/srv/runner/instance/slappartX/var/tests/`). After making the necessary changes, you can relaunch the tests by running `./bin/runTestSuite`.
## FAQ
Q1. What is the difference between `slapos-sr-testing` and `end-to-end-testing`?
- slapos-sr-testing requests SRs on a slapproxy in an SlapOSStandalone inside the slapos-sr-testing instance (same kind of thing as Theia or webrunner).
- end-to-end-testing requests SRs on the actual master, in real compute nodes (COMP-XXX). To do this it needs a slapos certificate which is passed as instance parameter to end-to-end-testing instance in the test suite.
So unlike slapos-sr-testing, end-to-end-testing does not contain the SRs it tests, it merely runs the python tests scripts (like mentioned `test_kvm.py`) and integrates with the ERP5 test suite. This also means we cannot access the files in the partition of the tested SRs, as those are on other computers. All we have access to is what a normal user requesting on panel would have access to.
[instance.cfg]
filename = instance.cfg.in
md5sum = 962830010e0a257d52c22141db3d34cf
import argparse
import configparser
import json
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--cfg', required=True)
parser.add_argument('-o', '--output', required=True)
args = parser.parse_args()
configp = configparser.ConfigParser()
configp.read(args.cfg)
with open(configp.get('slapconsole', 'cert_file')) as f:
crt = f.read()
with open(configp.get('slapconsole', 'key_file')) as f:
key = f.read()
url = configp.get('slapos', 'master_url')
with open(args.output, 'w') as f:
json.dump(
{
'client.crt': crt,
'client.key': key,
'master-url': url
}, f, indent=2)
if __name__ == '__main__':
main()
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"client.crt": {
"type": "string",
"default": "Certificate:\ndefault-client-crt\n-----END CERTIFICATE-----\n"
},
"client.key": {
"type": "string",
"default": "-----BEGIN PRIVATE KEY-----\ndefault-client-key\n-----END PRIVATE KEY-----\n"
},
"master-url": {
"type": "string",
"format": "uri",
"default": "https://slap.vifib.com"
},
"tests": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string",
"format": "uri"
},
"md5sum": {
"type": "string"
}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
}
{
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Values returned by End to End Testing instanciation",
"additionalProperties": false,
"properties": {},
"type": "object"
}
[buildout]
eggs-directory = ${buildout:eggs-directory}
develop-eggs-directory = ${buildout:develop-eggs-directory}
extends =
${nxdtest-instance.cfg:output}
parts =
.nxdtest
[slap-configuration]
recipe = slapos.cookbook:slapconfiguration.serialised
computer = $${slap-connection:computer-id}
partition = $${slap-connection:partition-id}
url = $${slap-connection:server-url}
key = $${slap-connection:key-file}
cert = $${slap-connection:cert-file}
[directory]
recipe = slapos.cookbook:mkdirectory
home = $${buildout:directory}
bin = $${buildout:directory}/bin
etc = $${buildout:directory}/etc
var = $${buildout:directory}/var
cfg = $${buildout:directory}/.slapos
nxdtestdir = $${:var}/nxdtest
log = $${:var}/log
[client.crt]
recipe = slapos.recipe.template
output = $${directory:cfg}/client.crt
inline = $${slap-configuration:configuration.client.crt}
[client.key]
recipe = slapos.recipe.template
output = $${directory:cfg}/client.key
inline = $${slap-configuration:configuration.client.key}
[slapos-client.cfg]
recipe = slapos.recipe.template
output = $${directory:cfg}/slapos-client.cfg
inline =
[slapos]
master_url = $${slap-configuration:configuration.master-url}
[slapconsole]
cert_file = $${client.crt:output}
key_file = $${client.key:output}
[env.sh]
recipe = slapos.recipe.template:jinja2
output = $${directory:cfg}/env.sh
inline =
export HOME=$${directory:home}
[runTestSuite]
# extended from stack/nxdtest
env.sh = $${env.sh:output}
workdir = $${directory:nxdtestdir}
[.nxdtest]
recipe = slapos.recipe.template:jinja2
output = $${runTestSuite:workdir}/.nxdtest
python_for_test = ${python_for_test:executable}
testdir = $${tests:location}
log-output = $${directory:log}/e2e-testing.log
context =
key python_for_test :python_for_test
key testdir :testdir
key slapos_cfg slapos-client.cfg:output
key log_file :log-output
inline =
import os
directory = "{{ testdir }}"
slapos_cfg = {{ repr(slapos_cfg) }}
log_file = {{ repr(log_file) }}
dir_list = os.listdir(directory)
for filename in dir_list:
name, ext = os.path.splitext(filename)
if name.startswith('test') and ext == '.py':
TestCase(
name,
[{{ repr(python_for_test) }} , '-m', 'unittest', '-v', name],
cwd=directory,
env={'SLAPOS_E2E_TEST_CLIENT_CFG': slapos_cfg,
'SLAPOS_E2E_TEST_LOG_FILE': log_file},
summaryf=UnitTest.summary,
)
[tests]
recipe = slapos.recipe.build
tests = $${slap-configuration:configuration.tests}
location = $${directory:var}/tests
install =
import os
os.mkdir(location)
buildout_offline = self.buildout['buildout']['offline']
try:
# Allow to do self.download() which can only be used in "online" mode
self.buildout['buildout']['offline'] = 'false'
for i, test in enumerate(options['tests']):
tmp = self.download(test['url'], test['md5sum'])
path = os.path.join(location, 'test%s.py' % i)
os.rename(tmp, path)
finally:
# reset the parameter
self.buildout['buildout']['offline'] = buildout_offline
[buildout]
extends =
../../component/pygolang/buildout.cfg
../../stack/slapos.cfg
../../stack/nxdtest.cfg
buildout.hash.cfg
parts =
instance.cfg
slapos-cookbook
[instance.cfg]
recipe = slapos.recipe.template
output = ${buildout:directory}/instance.cfg
url = ${:_profile_base_location_}/${:filename}
[e2e.py]
recipe = slapos.recipe.build:download
output = ${buildout:directory}/${:filename}
url = ${:_profile_base_location_}/${:filename}
[python_for_test]
<= python-interpreter
interpreter = python_for_test
executable = ${buildout:bin-directory}/${:interpreter}
depends = ${lxml-python:egg}
eggs =
${pygolang:egg}
slapos.core
websocket-client
requests
opcua
[versions]
websocket-client = 1.4.2
opcua = 0.98.13
{
"name": "End-To-End-Testing",
"description": "End-To-End Testing on SlapOS Cloud",
"serialisation": "json-in-xml",
"software-type": {
"default": {
"title": "default",
"software-type": "default",
"description": "default",
"request": "instance-input-schema.json",
"response": "instance-output-schema.json"
}
}
}
import slapos.testing.e2e as e2e
import time
class HealthTest(e2e.EndToEndTestCase):
@classmethod
def test_health_promise_feed(self):
instance_name = e2e.time.strftime('e2e-test-health-%Y-%B-%d-%H:%M:%S')
product = self.product.slapmonitor
parameter_dict = {}
self.request(
self.product.slapmonitor,
instance_name,
software_type='default',
filter_kw={"computer_guid": "COMP-4057"})
self.waitUntilGreen(instance_name)
self.connection_dict = self.getInstanceInfos(instance_name).connection_dict
resp, url = self.waitUntilMonitorURLReady(instance_name=instance_name)
self.getMonitorPromises(resp.content)
import slapos.testing.e2e as e2e
class KvmTest(e2e.EndToEndTestCase):
def test(self):
instance_name = e2e.time.strftime('e2e-test-kvm-%Y-%B-%d-%H:%M:%S')
# instance_name = 'e2e-kvm-test' # avoid timestamp to reuse instance
self.request(self.product.kvm, instance_name)
self.waitUntilGreen(instance_name)
connection_dict = self.request(self.product.kvm, instance_name)
self.assertIn('url', connection_dict)
import unittest
class Test(unittest.TestCase):
def test_fail(self):
self.assertEqual(0, 1)
def test_succeed(self):
self.assertEqual(0, 0)
...@@ -103,6 +103,10 @@ ...@@ -103,6 +103,10 @@
"null" "null"
] ]
}, },
"with-max-rlimit-nofile": {
"description": "Set open file descriptors soft limit to hard limit",
"type": "boolean"
},
"family-override": { "family-override": {
"description": "Family-wide options, possibly overriding global options", "description": "Family-wide options, possibly overriding global options",
"default": {}, "default": {},
......
...@@ -25,12 +25,26 @@ ...@@ -25,12 +25,26 @@
# #
############################################################################## ##############################################################################
import hashlib
import itertools import itertools
import json import json
import os import os
import shutil
import subprocess
import sys import sys
import tempfile
import time
import urllib
from slapos.testing.testcase import makeModuleSetUpAndTestCaseClass import requests
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID
from slapos.testing.testcase import ManagedResource, makeModuleSetUpAndTestCaseClass
from slapos.testing.utils import findFreeTCPPort
_setUpModule, SlapOSInstanceTestCase = makeModuleSetUpAndTestCaseClass( _setUpModule, SlapOSInstanceTestCase = makeModuleSetUpAndTestCaseClass(
...@@ -200,3 +214,175 @@ class ERP5InstanceTestCase(SlapOSInstanceTestCase, metaclass=ERP5InstanceTestMet ...@@ -200,3 +214,175 @@ class ERP5InstanceTestCase(SlapOSInstanceTestCase, metaclass=ERP5InstanceTestMet
def getComputerPartitionPath(cls, partition_reference): def getComputerPartitionPath(cls, partition_reference):
partition_id = cls.getComputerPartition(partition_reference).getId() partition_id = cls.getComputerPartition(partition_reference).getId()
return os.path.join(cls.slap._instance_root, partition_id) return os.path.join(cls.slap._instance_root, partition_id)
class CaucaseService(ManagedResource):
"""A caucase service.
"""
url: str = None
directory: str = None
_caucased_process: subprocess.Popen = None
def open(self) -> None:
# start a caucased and server certificate.
software_release_root_path = os.path.join(
self._cls.slap._software_root,
hashlib.md5(self._cls.getSoftwareURL().encode()).hexdigest(),
)
caucased_path = os.path.join(software_release_root_path, 'bin', 'caucased')
self.directory = tempfile.mkdtemp()
caucased_dir = os.path.join(self.directory, 'caucased')
os.mkdir(caucased_dir)
os.mkdir(os.path.join(caucased_dir, 'user'))
os.mkdir(os.path.join(caucased_dir, 'service'))
backend_caucased_netloc = f'{self._cls._ipv4_address}:{findFreeTCPPort(self._cls._ipv4_address)}'
self.url = 'http://' + backend_caucased_netloc
self._caucased_process = subprocess.Popen(
[
caucased_path,
'--db', os.path.join(caucased_dir, 'caucase.sqlite'),
'--server-key', os.path.join(caucased_dir, 'server.key.pem'),
'--netloc', backend_caucased_netloc,
'--service-auto-approve-count', '1',
],
# capture subprocess output not to pollute test's own stdout
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
for _ in range(30):
try:
if requests.get(self.url).status_code == 200:
break
except Exception:
pass
time.sleep(1)
else:
raise RuntimeError('caucased failed to start.')
def close(self) -> None:
self._caucased_process.terminate()
self._caucased_process.wait()
self._caucased_process.stdout.close()
shutil.rmtree(self.directory)
@property
def ca_crt_path(self) -> str:
"""Path of the CA certificate from this caucase.
"""
ca_crt_path = os.path.join(self.directory, 'ca.crt.pem')
if not os.path.exists(ca_crt_path):
with open(ca_crt_path, 'w') as f:
f.write(
requests.get(urllib.parse.urljoin(
self.url,
'/cas/crt/ca.crt.pem',
)).text)
return ca_crt_path
class CaucaseCertificate(ManagedResource):
"""A certificate signed by a caucase service.
"""
ca_crt_file: str = None
crl_file: str = None
csr_file: str = None
cert_file: str = None
key_file: str = None
def open(self) -> None:
self.tmpdir = tempfile.mkdtemp()
self.ca_crt_file = os.path.join(self.tmpdir, 'ca-crt.pem')
self.crl_file = os.path.join(self.tmpdir, 'ca-crl.pem')
self.csr_file = os.path.join(self.tmpdir, 'csr.pem')
self.cert_file = os.path.join(self.tmpdir, 'crt.pem')
self.key_file = os.path.join(self.tmpdir, 'key.pem')
def close(self) -> None:
shutil.rmtree(self.tmpdir)
@property
def _caucase_path(self) -> str:
"""path of caucase executable.
"""
software_release_root_path = os.path.join(
self._cls.slap._software_root,
hashlib.md5(self._cls.getSoftwareURL().encode()).hexdigest(),
)
return os.path.join(software_release_root_path, 'bin', 'caucase')
def request(self, common_name: str, caucase: CaucaseService) -> None:
"""Generate certificate and request signature to the caucase service.
This overwrite any previously requested certificate for this instance.
"""
cas_args = [
self._caucase_path,
'--ca-url', caucase.url,
'--ca-crt', self.ca_crt_file,
'--crl', self.crl_file,
]
key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
with open(self.key_file, 'wb') as f:
f.write(
key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption(),
))
csr = x509.CertificateSigningRequestBuilder().subject_name(
x509.Name([
x509.NameAttribute(
NameOID.COMMON_NAME,
common_name,
),
])).sign(
key,
hashes.SHA256(),
default_backend(),
)
with open(self.csr_file, 'wb') as f:
f.write(csr.public_bytes(serialization.Encoding.PEM))
csr_id = subprocess.check_output(
cas_args + [
'--send-csr', self.csr_file,
],
).split()[0].decode()
assert csr_id
for _ in range(30):
if not subprocess.call(
cas_args + [
'--get-crt', csr_id, self.cert_file,
],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
) == 0:
break
else:
time.sleep(1)
else:
raise RuntimeError('getting service certificate failed.')
with open(self.cert_file) as cert_file:
assert 'BEGIN CERTIFICATE' in cert_file.read()
def revoke(self, caucase: CaucaseService) -> None:
"""Revoke the client certificate on this caucase instance.
"""
subprocess.check_call([
self._caucase_path,
'--ca-url', caucase.url,
'--ca-crt', self.ca_crt_file,
'--crl', self.crl_file,
'--revoke-crt', self.cert_file, self.key_file,
])
This diff is collapsed.
...@@ -32,6 +32,7 @@ import glob ...@@ -32,6 +32,7 @@ import glob
import http.client import http.client
import json import json
import os import os
import resource
import shutil import shutil
import socket import socket
import sqlite3 import sqlite3
...@@ -633,8 +634,7 @@ class ZopeTestMixin(ZopeSkinsMixin, CrontabMixin): ...@@ -633,8 +634,7 @@ class ZopeTestMixin(ZopeSkinsMixin, CrontabMixin):
)): )):
os.unlink(logfile) os.unlink(logfile)
def _getCrontabCommand(self, crontab_name): def _getCrontabCommand(self, crontab_name: str) -> str:
# type: (str) -> str
"""Read a crontab and return the command that is executed. """Read a crontab and return the command that is executed.
overloaded to use crontab from zope partition overloaded to use crontab from zope partition
...@@ -1045,8 +1045,7 @@ class TestNEO(ZopeSkinsMixin, CrontabMixin, ERP5InstanceTestCase): ...@@ -1045,8 +1045,7 @@ class TestNEO(ZopeSkinsMixin, CrontabMixin, ERP5InstanceTestCase):
__partition_reference__ = 'n' __partition_reference__ = 'n'
__test_matrix__ = matrix((neo,)) __test_matrix__ = matrix((neo,))
def _getCrontabCommand(self, crontab_name): def _getCrontabCommand(self, crontab_name: str) -> str:
# type: (str) -> str
"""Read a crontab and return the command that is executed. """Read a crontab and return the command that is executed.
overloaded to use crontab from neo partition overloaded to use crontab from neo partition
...@@ -1100,3 +1099,36 @@ class TestNEO(ZopeSkinsMixin, CrontabMixin, ERP5InstanceTestCase): ...@@ -1100,3 +1099,36 @@ class TestNEO(ZopeSkinsMixin, CrontabMixin, ERP5InstanceTestCase):
'var', 'var',
'log', 'log',
f)) f))
class TestWithMaxRlimitNofileParameter(ERP5InstanceTestCase, TestPublishedURLIsReachableMixin):
"""Test setting the with-max-rlimit-nofile parameter sets the open fd soft limit to the hard limit.
"""
__partition_reference__ = 'nf'
@classmethod
def getInstanceParameterDict(cls):
return {'_': json.dumps({'with-max-rlimit-nofile': True})}
def test_with_max_rlimit_nofile(self):
with self.slap.instance_supervisor_rpc as supervisor:
all_process_info = supervisor.getAllProcessInfo()
_, current_hard_limit = resource.getrlimit(resource.RLIMIT_NOFILE)
process_info, = (p for p in all_process_info if p['name'].startswith('zope-'))
self.assertEqual(
resource.prlimit(process_info['pid'], resource.RLIMIT_NOFILE),
(current_hard_limit, current_hard_limit))
class TestUnsetWithMaxRlimitNofileParameter(ERP5InstanceTestCase, TestPublishedURLIsReachableMixin):
"""Test not setting the with-max-rlimit-nofile parameter doesn't change the soft limit of erp5
"""
__partition_reference__ = 'nnf'
def test_unset_with_max_rlimit_nofile(self) -> None:
with self.slap.instance_supervisor_rpc as supervisor:
all_process_info = supervisor.getAllProcessInfo()
limit = resource.getrlimit(resource.RLIMIT_NOFILE)
process_info, = (p for p in all_process_info if p['name'].startswith('zope-'))
self.assertEqual(
resource.prlimit(process_info['pid'], resource.RLIMIT_NOFILE), limit)
...@@ -36,6 +36,7 @@ import subprocess ...@@ -36,6 +36,7 @@ import subprocess
import urllib.parse import urllib.parse
import MySQLdb import MySQLdb
import MySQLdb.connections
from slapos.testing.utils import CrontabMixin, getPromisePluginParameterDict from slapos.testing.utils import CrontabMixin, getPromisePluginParameterDict
...@@ -60,8 +61,7 @@ class MariaDBTestCase(ERP5InstanceTestCase): ...@@ -60,8 +61,7 @@ class MariaDBTestCase(ERP5InstanceTestCase):
return "mariadb" return "mariadb"
@classmethod @classmethod
def _getInstanceParameterDict(cls): def _getInstanceParameterDict(cls) -> dict:
# type: () -> dict
return { return {
'tcpv4-port': 3306, 'tcpv4-port': 3306,
'max-connection-count': 5, 'max-connection-count': 5,
...@@ -76,12 +76,10 @@ class MariaDBTestCase(ERP5InstanceTestCase): ...@@ -76,12 +76,10 @@ class MariaDBTestCase(ERP5InstanceTestCase):
} }
@classmethod @classmethod
def getInstanceParameterDict(cls): def getInstanceParameterDict(cls) -> dict:
# type: () -> dict
return {'_': json.dumps(cls._getInstanceParameterDict())} return {'_': json.dumps(cls._getInstanceParameterDict())}
def getDatabaseConnection(self): def getDatabaseConnection(self) -> MySQLdb.connections.Connection:
# type: () -> MySQLdb.connections.Connection
connection_parameter_dict = json.loads( connection_parameter_dict = json.loads(
self.computer_partition.getConnectionParameterDict()['_']) self.computer_partition.getConnectionParameterDict()['_'])
db_url = urllib.parse.urlparse(connection_parameter_dict['database-list'][0]) db_url = urllib.parse.urlparse(connection_parameter_dict['database-list'][0])
...@@ -106,8 +104,7 @@ class TestCrontabs(MariaDBTestCase, CrontabMixin): ...@@ -106,8 +104,7 @@ class TestCrontabs(MariaDBTestCase, CrontabMixin):
'*/srv/backup/*', '*/srv/backup/*',
) )
def test_full_backup(self): def test_full_backup(self) -> None:
# type: () -> None
self._executeCrontabAtDate('mariadb-backup', '2050-01-01') self._executeCrontabAtDate('mariadb-backup', '2050-01-01')
full_backup_file, = glob.glob( full_backup_file, = glob.glob(
os.path.join( os.path.join(
...@@ -121,8 +118,7 @@ class TestCrontabs(MariaDBTestCase, CrontabMixin): ...@@ -121,8 +118,7 @@ class TestCrontabs(MariaDBTestCase, CrontabMixin):
with gzip.open(full_backup_file, 'rt') as dump: with gzip.open(full_backup_file, 'rt') as dump:
self.assertIn('CREATE TABLE', dump.read()) self.assertIn('CREATE TABLE', dump.read())
def test_logrotate_and_slow_query_digest(self): def test_logrotate_and_slow_query_digest(self) -> None:
# type: () -> None
# slow query digest needs to run after logrotate, since it operates on the rotated # slow query digest needs to run after logrotate, since it operates on the rotated
# file, so this tests both logrotate and slow query digest. # file, so this tests both logrotate and slow query digest.
...@@ -193,8 +189,7 @@ class TestCrontabs(MariaDBTestCase, CrontabMixin): ...@@ -193,8 +189,7 @@ class TestCrontabs(MariaDBTestCase, CrontabMixin):
class TestMariaDB(MariaDBTestCase): class TestMariaDB(MariaDBTestCase):
def test_utf8_collation(self): def test_utf8_collation(self) -> None:
# type: () -> None
cnx = self.getDatabaseConnection() cnx = self.getDatabaseConnection()
with contextlib.closing(cnx): with contextlib.closing(cnx):
cnx.query( cnx.query(
...@@ -219,8 +214,7 @@ class TestMariaDB(MariaDBTestCase): ...@@ -219,8 +214,7 @@ class TestMariaDB(MariaDBTestCase):
class TestMroonga(MariaDBTestCase): class TestMroonga(MariaDBTestCase):
def test_mroonga_plugin_loaded(self): def test_mroonga_plugin_loaded(self) -> None:
# type: () -> None
cnx = self.getDatabaseConnection() cnx = self.getDatabaseConnection()
with contextlib.closing(cnx): with contextlib.closing(cnx):
cnx.query("show plugins") cnx.query("show plugins")
...@@ -229,8 +223,7 @@ class TestMroonga(MariaDBTestCase): ...@@ -229,8 +223,7 @@ class TestMroonga(MariaDBTestCase):
('Mroonga', 'ACTIVE', 'STORAGE ENGINE', 'ha_mroonga.so', 'GPL'), ('Mroonga', 'ACTIVE', 'STORAGE ENGINE', 'ha_mroonga.so', 'GPL'),
plugins) plugins)
def test_mroonga_normalize_udf(self): def test_mroonga_normalize_udf(self) -> None:
# type: () -> None
# example from https://mroonga.org/docs/reference/udf/mroonga_normalize.html#usage # example from https://mroonga.org/docs/reference/udf/mroonga_normalize.html#usage
cnx = self.getDatabaseConnection() cnx = self.getDatabaseConnection()
with contextlib.closing(cnx): with contextlib.closing(cnx):
...@@ -255,8 +248,7 @@ class TestMroonga(MariaDBTestCase): ...@@ -255,8 +248,7 @@ class TestMroonga(MariaDBTestCase):
self.assertEqual((('ABCDあぃうぇ㍑'.encode(),),), self.assertEqual((('ABCDあぃうぇ㍑'.encode(),),),
cnx.store_result().fetch_row(maxrows=2)) cnx.store_result().fetch_row(maxrows=2))
def test_mroonga_full_text_normalizer(self): def test_mroonga_full_text_normalizer(self) -> None:
# type: () -> None
# example from https://mroonga.org//docs/tutorial/storage.html#how-to-specify-the-normalizer # example from https://mroonga.org//docs/tutorial/storage.html#how-to-specify-the-normalizer
cnx = self.getDatabaseConnection() cnx = self.getDatabaseConnection()
with contextlib.closing(cnx): with contextlib.closing(cnx):
...@@ -293,8 +285,7 @@ class TestMroonga(MariaDBTestCase): ...@@ -293,8 +285,7 @@ class TestMroonga(MariaDBTestCase):
cnx.store_result().fetch_row(maxrows=2), cnx.store_result().fetch_row(maxrows=2),
) )
def test_mroonga_full_text_normalizer_TokenBigramSplitSymbolAlphaDigit(self): def test_mroonga_full_text_normalizer_TokenBigramSplitSymbolAlphaDigit(self) -> None:
# type: () -> None
# Similar to as ERP5's testI18NSearch with erp5_full_text_mroonga_catalog # Similar to as ERP5's testI18NSearch with erp5_full_text_mroonga_catalog
cnx = self.getDatabaseConnection() cnx = self.getDatabaseConnection()
with contextlib.closing(cnx): with contextlib.closing(cnx):
...@@ -337,8 +328,7 @@ class TestMroonga(MariaDBTestCase): ...@@ -337,8 +328,7 @@ class TestMroonga(MariaDBTestCase):
""") """)
self.assertEqual(((1,),), cnx.store_result().fetch_row(maxrows=2)) self.assertEqual(((1,),), cnx.store_result().fetch_row(maxrows=2))
def test_mroonga_full_text_stem(self): def test_mroonga_full_text_stem(self) -> None:
# type: () -> None
# example from https://mroonga.org//docs/tutorial/storage.html#how-to-specify-the-token-filters # example from https://mroonga.org//docs/tutorial/storage.html#how-to-specify-the-token-filters
cnx = self.getDatabaseConnection() cnx = self.getDatabaseConnection()
with contextlib.closing(cnx): with contextlib.closing(cnx):
......
...@@ -46,7 +46,7 @@ from slapos.testing.testcase import ( ...@@ -46,7 +46,7 @@ from slapos.testing.testcase import (
makeModuleSetUpAndTestCaseClass, makeModuleSetUpAndTestCaseClass,
) )
old_software_release_url = 'https://lab.nexedi.com/nexedi/slapos/raw/1.0.167.9/software/erp5/software.cfg' old_software_release_url = 'https://lab.nexedi.com/nexedi/slapos/raw/1.0.167.10/software/erp5/software.cfg'
new_software_release_url = os.path.abspath( new_software_release_url = os.path.abspath(
os.path.join(os.path.dirname(__file__), '..', 'software.cfg')) os.path.join(os.path.dirname(__file__), '..', 'software.cfg'))
......
...@@ -92,8 +92,8 @@ class WendelinTutorialTestCase(FluentdTestCase): ...@@ -92,8 +92,8 @@ class WendelinTutorialTestCase(FluentdTestCase):
@classmethod @classmethod
def measureDict(cls): def measureDict(cls):
return {k: v.encode() for k, v in return {k: v for k, v in
zip((b'pressure', b'humidity', b'temperature'), cls._measurementList)} zip(('pressure', 'humidity', 'temperature'), cls._measurementList)}
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
......
...@@ -27,4 +27,4 @@ md5sum = 98faa5ad8cfb23a11d97a459078a1d05 ...@@ -27,4 +27,4 @@ md5sum = 98faa5ad8cfb23a11d97a459078a1d05
[template-runTestSuite] [template-runTestSuite]
filename = runTestSuite.in filename = runTestSuite.in
md5sum = 2bb3d71a0e04bc8bc828bb3f726ef3ff md5sum = 5db53d622bd68fb07e078ddc4403a240
...@@ -93,7 +93,7 @@ def main(): ...@@ -93,7 +93,7 @@ def main():
firefox_capabilities['marionette'] = True firefox_capabilities['marionette'] = True
browser = webdriver.Firefox( browser = webdriver.Firefox(
capabilities=firefox_capabilities, capabilities=firefox_capabilities,
firefox_binary='${firefox-wrapper-68:location}', firefox_binary='${firefox-wrapper:location}',
executable_path='${geckodriver:location}') executable_path='${geckodriver:location}')
else: else:
assert target == 'selenium-server', f"Unsupported target {test_runner['target']}" assert target == 'selenium-server', f"Unsupported target {test_runner['target']}"
......
...@@ -22,7 +22,7 @@ parts = ...@@ -22,7 +22,7 @@ parts =
git git
eggs eggs
xserver xserver
firefox-68 firefox
xwd xwd
renderjs-install renderjs-install
jio-install jio-install
......
[instance-profile] [instance-profile]
filename = instance.cfg.in filename = instance.cfg.in
md5sum = 0d50ed911a41b76b952b63d37853c3a4 md5sum = f753802ad631a57c559d868e525cf81b
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"mb_password_complexity": { "mb-password-complexity": {
"title": "Password complexity", "title": "Password complexity",
"description": "Check Metabase documentation for more details.", "description": "Check Metabase documentation for more details.",
"type": "string", "type": "string",
...@@ -14,11 +14,25 @@ ...@@ -14,11 +14,25 @@
"strong" "strong"
] ]
}, },
"mb_password_length": { "mb-password-length": {
"title": "Password length", "title": "Password length",
"description": "Password length", "description": "Password length",
"type": "integer", "type": "integer",
"default": 6 "default": 6
},
"mb-aggregated-query-row-limit": {
"title": "Aggregated row limit",
"description": "Maximum number of rows to return for aggregated queries via the API.",
"type": "integer",
"default": 10000,
"maximum": 1048575
},
"mb-unaggregated-query-row-limit": {
"title": "Unaggregated row limit",
"description": "Maximum number of rows to return specifically on `:rows`-type queries via the API. Must be less than the number configured in `mb-aggregated-query-row-limit`",
"type": "integer",
"default": 2000,
"maximum": 1048575
} }
} }
} }
...@@ -31,8 +31,16 @@ slapparameter-dict = $${slap-configuration:configuration} ...@@ -31,8 +31,16 @@ slapparameter-dict = $${slap-configuration:configuration}
home = $${buildout:directory} home = $${buildout:directory}
init = init =
default_parameters = options.get('slapparameter-dict') default_parameters = options.get('slapparameter-dict')
options['mb_password_complexity'] = default_parameters.get('mb_password_complexity', 'normal') options['mb-password-complexity'] = default_parameters.get(
options['mb_password_length'] = default_parameters.get('mb_password_length', '6') 'mb-password-complexity',
default_parameters.get('mb_password_complexity', 'normal'))
options['mb-password-length'] = default_parameters.get(
'mb-password-length',
default_parameters.get('mb_password_length', '6'))
options['mb-aggregated-query-row-limit'] = default_parameters.get(
'mb-aggregated-query-row-limit', '10000')
options['mb-unaggregated-query-row-limit'] = default_parameters.get(
'mb-unaggregated-query-row-limit', '2000')
[metabase-instance] [metabase-instance]
recipe = slapos.cookbook:wrapper recipe = slapos.cookbook:wrapper
...@@ -51,8 +59,10 @@ environment = ...@@ -51,8 +59,10 @@ environment =
MB_DB_USER=$${postgresql:superuser} MB_DB_USER=$${postgresql:superuser}
MB_DB_PASS=$${postgresql:password} MB_DB_PASS=$${postgresql:password}
MB_DB_HOST=$${postgresql:ipv4} MB_DB_HOST=$${postgresql:ipv4}
MB_PASSWORD_COMPLEXITY=$${slap-parameter:mb_password_complexity} MB_PASSWORD_COMPLEXITY=$${slap-parameter:mb-password-complexity}
MB_PASSWORD_LENGTH=$${slap-parameter:mb_password_length} MB_PASSWORD_LENGTH=$${slap-parameter:mb-password-length}
MB_AGGREGATED_QUERY_ROW_LIMIT=$${slap-parameter:mb-aggregated-query-row-limit}
MB_UNAGGREGATED_QUERY_ROW_LIMIT=$${slap-parameter:mb-unaggregated-query-row-limit}
FONTCONFIG_FILE=$${fontconfig-conf:output} FONTCONFIG_FILE=$${fontconfig-conf:output}
JAVA_ARGS=-Dorg.quartz.scheduler.instanceId=$${slap-connection:computer-id}.$${slap-connection:partition-id} -Djava.io.tmpdir="$${directory:tmp}" JAVA_ARGS=-Dorg.quartz.scheduler.instanceId=$${slap-connection:computer-id}.$${slap-connection:partition-id} -Djava.io.tmpdir="$${directory:tmp}"
hash-existing-files = hash-existing-files =
......
...@@ -20,8 +20,8 @@ parts = ...@@ -20,8 +20,8 @@ parts =
[metabase.jar] [metabase.jar]
recipe = slapos.recipe.build:download recipe = slapos.recipe.build:download
url = https://downloads.metabase.com/v0.47.0/metabase.jar url = https://downloads.metabase.com/v0.48.2/metabase.jar
md5sum = b81c71668a2177d89690730fabd85d9e md5sum = d708a85436da3d5751f0e48ebd10c142
[instance-profile] [instance-profile]
recipe = slapos.recipe.template recipe = slapos.recipe.template
......
...@@ -14,4 +14,4 @@ ...@@ -14,4 +14,4 @@
# not need these here). # not need these here).
[template-instance] [template-instance]
filename = instance.cfg filename = instance.cfg
md5sum = 7a558c2b9461ec588c9d77bdeef64e4d md5sum = 5a765463118f8b2a09df6260f56c2175
...@@ -25,7 +25,7 @@ develop-eggs-directory = {{ develop_eggs_directory }} ...@@ -25,7 +25,7 @@ develop-eggs-directory = {{ develop_eggs_directory }}
# needed for the "repo" command (to download many git repositories) # needed for the "repo" command (to download many git repositories)
recipe = slapos.recipe.build:gitclone recipe = slapos.recipe.build:gitclone
repository = https://chromium.googlesource.com/chromium/tools/depot_tools.git repository = https://chromium.googlesource.com/chromium/tools/depot_tools.git
branch = master branch = main
git-executable = {{ git_path }}/bin/git git-executable = {{ git_path }}/bin/git
[customize-path] [customize-path]
......
...@@ -14,12 +14,12 @@ ...@@ -14,12 +14,12 @@
# not need these here). # not need these here).
[template-nextcloud-install.sh] [template-nextcloud-install.sh]
filename = nextcloud-install.sh.in filename = nextcloud-install.sh.in
md5sum = 965cc84d4c8e39f06850fac361575647 md5sum = f31dfd6fce79fcf1c13cbd96dd366492
[template-nextcloud-config.json] [template-nextcloud-config.json]
filename = nextcloud-config.json.in filename = nextcloud-config.json.in
md5sum = 6f42f0a8c5e5c0c657541a65c4d9ee57 md5sum = 133ad47aec7e16f716eb710ef38823e8
[template-nextcloud-instance] [template-nextcloud-instance]
filename = nextcloud-instance.cfg.in filename = nextcloud-instance.cfg.in
md5sum = a59b081bd39f61c7361fdb6c54fc2039 md5sum = 65d2fef4aa41fa70e5194d73a8cb2c4a
...@@ -35,6 +35,19 @@ ...@@ -35,6 +35,19 @@
"timeout": 0 "timeout": 0
}, },
"logfile": "{{ parameter_dict['data-dir'] }}/nextcloud.log", "logfile": "{{ parameter_dict['data-dir'] }}/nextcloud.log",
"datadirectory": "{{ parameter_dict['data-dir'] }}" "loglevel" => 2,
"datadirectory": "{{ parameter_dict['data-dir'] }}",
"preview_ffmpeg_path": "{{ parameter_dict['ffmpeg-path'] }}",
"tempdirectory": "{{ parameter_dict['tmp-dir'] }}",
"apps_paths": [
{
"path": "{{ parameter_dict['nextcloud'] }}/apps",
"url": "/apps",
"writable": true
}
],
"default_phone_region": "FR",
"default_locale": "fr_FR",
"default_timezone": "Europe/Paris"
} }
} }
...@@ -115,19 +115,9 @@ ...@@ -115,19 +115,9 @@
"type": "string", "type": "string",
"format": "uri" "format": "uri"
}, },
"instance.trusted-domain-1": { "instance.trusted-domain-list": {
"title": "Authorized domain on nextcloud", "title": "Authorized domain(s) on nextcloud",
"description": "Trusted domain used to connect to Nextcloud instance.", "description": "Trusted domain(s) used to connect to Nextcloud instance. Space separated.",
"type": "string"
},
"instance.trusted-domain-2": {
"title": "Second authorized domain on nextcloud",
"description": "Trusted domain used to connect to Nextcloud instance.",
"type": "string"
},
"instance.trusted-domain-3": {
"title": "Third authorized domain on nextcloud",
"description": "Trusted domain used to connect to Nextcloud instance.",
"type": "string" "type": "string"
}, },
"instance.trusted-proxy-list": { "instance.trusted-proxy-list": {
......
...@@ -140,4 +140,4 @@ if [ -f "{{ parameter_dict['nextcloud'] }}/config/CAN_INSTALL" ]; then ...@@ -140,4 +140,4 @@ if [ -f "{{ parameter_dict['nextcloud'] }}/config/CAN_INSTALL" ]; then
rm {{ parameter_dict['nextcloud'] }}/config/CAN_INSTALL rm {{ parameter_dict['nextcloud'] }}/config/CAN_INSTALL
fi fi
date > {{ parameter_dict['nextcloud'] }}/.slapos-install-done date > {{ parameter_dict['installed-file'] }}
...@@ -8,6 +8,7 @@ redis = ${directory:srv}/redis ...@@ -8,6 +8,7 @@ redis = ${directory:srv}/redis
redis-log = ${directory:log}/redis redis-log = ${directory:log}/redis
data = ${directory:srv}/data data = ${directory:srv}/data
backup = ${directory:backup}/nextcloud backup = ${directory:backup}/nextcloud
tmp = ${buildout:directory}/tmp
[service-redis] [service-redis]
recipe = slapos.cookbook:redis.server recipe = slapos.cookbook:redis.server
...@@ -42,13 +43,15 @@ rotate-num = 30 ...@@ -42,13 +43,15 @@ rotate-num = 30
[instance-parameter] [instance-parameter]
nextcloud = ${:document-root} nextcloud = ${:document-root}
installed-file = ${directory:etc}/.nextcloud-install-done
admin-user = admin admin-user = admin
admin-password = admin admin-password = admin
ffmpeg-path = {{ ffmpeg_location }}/bin/ffmpeg
tmp-dir = ${nc-directory:tmp}
trusted-domain-list = trusted-domain-list =
[${apache-php-configuration:ip}]:${apache-php-configuration:port} [${apache-php-configuration:ip}]:${apache-php-configuration:port}
${slap-parameter:instance.trusted-domain-1} ${request-frontend:connection-domain}
${slap-parameter:instance.trusted-domain-2} ${slap-parameter:instance.trusted-domain-list}
${slap-parameter:instance.trusted-domain-3}
trusted-proxy-list = ${slap-parameter:instance.trusted-proxy-list} trusted-proxy-list = ${slap-parameter:instance.trusted-proxy-list}
cli-url = ${slap-parameter:instance.cli-url} cli-url = ${slap-parameter:instance.cli-url}
...@@ -69,6 +72,9 @@ collabora-url = ${slap-parameter:instance.collabora-url} ...@@ -69,6 +72,9 @@ collabora-url = ${slap-parameter:instance.collabora-url}
stun-server = ${slap-parameter:instance.stun-server} stun-server = ${slap-parameter:instance.stun-server}
turn-server = ${slap-parameter:instance.turn-server} turn-server = ${slap-parameter:instance.turn-server}
turn-secret = ${slap-parameter:instance.turn-secret} turn-secret = ${slap-parameter:instance.turn-secret}
# php.ini
php.opcache.revalidate-freq = 60
php.opcache.interned-strings-buffer = 24
[nextcloud-install.sh] [nextcloud-install.sh]
recipe = slapos.recipe.template:jinja2 recipe = slapos.recipe.template:jinja2
...@@ -105,7 +111,7 @@ input = inline:#!/bin/bash ...@@ -105,7 +111,7 @@ input = inline:#!/bin/bash
echo "Nextcloud is not installed."; echo "Nextcloud is not installed.";
exit 1; exit 1;
fi fi
if [ ! -f "${instance-parameter:nextcloud}/.slapos-install-done" ]; then if [ ! -f "${instance-parameter:installed-file}" ]; then
echo "Nextcloud is not configured."; echo "Nextcloud is not configured.";
exit 1; exit 1;
fi fi
...@@ -203,7 +209,5 @@ instance.turn-server = ...@@ -203,7 +209,5 @@ instance.turn-server =
instance.turn-secret = instance.turn-secret =
instance.cli-url = ${apache-php-configuration:url} instance.cli-url = ${apache-php-configuration:url}
instance.trusted-domain-1 =
instance.trusted-domain-2 =
instance.trusted-domain-3 =
instance.trusted-proxy-list = instance.trusted-proxy-list =
instance.trusted-domain-list =
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
extends = extends =
buildout.hash.cfg buildout.hash.cfg
../../component/redis/buildout.cfg ../../component/redis/buildout.cfg
../../component/ffmpeg/buildout.cfg
../../stack/lamp/buildout.cfg ../../stack/lamp/buildout.cfg
[nc-download] [nc-download]
...@@ -28,6 +29,7 @@ context = ...@@ -28,6 +29,7 @@ context =
key python3_location python3:location key python3_location python3:location
key news_updater_location news-updater:location key news_updater_location news-updater:location
key php_location apache-php:location key php_location apache-php:location
key ffmpeg_location ffmpeg:location
raw redis_bin ${redis:location}/bin/redis-server raw redis_bin ${redis:location}/bin/redis-server
raw redis_cli ${redis:location}/bin/redis-cli raw redis_cli ${redis:location}/bin/redis-cli
key nextcloud_install_sh template-nextcloud-install.sh:target key nextcloud_install_sh template-nextcloud-install.sh:target
...@@ -42,7 +44,6 @@ db-user = nextcloud ...@@ -42,7 +44,6 @@ db-user = nextcloud
[nc-download-unpacked] [nc-download-unpacked]
recipe = slapos.recipe.build:download-unpacked recipe = slapos.recipe.build:download-unpacked
shared = true
[news-updater] [news-updater]
<= nc-download-unpacked <= nc-download-unpacked
...@@ -66,8 +67,8 @@ md5sum = 88adcbc34ef7e461f515ba96b82365d9 ...@@ -66,8 +67,8 @@ md5sum = 88adcbc34ef7e461f515ba96b82365d9
[nextcloud-app-snappymail] [nextcloud-app-snappymail]
<= nc-download-unpacked <= nc-download-unpacked
url = https://snappymail.eu/repository/nextcloud/snappymail-2.29.1-nextcloud.tar.gz url = https://snappymail.eu/repository/nextcloud/snappymail-2.29.4-nextcloud.tar.gz
md5sum = b7500ea4e089d8a9e3fa381d6df3a3b0 md5sum = 676bf0fa3b9f0fb9f0208304cf302a26
[nextcloud-app-news] [nextcloud-app-news]
<= nc-download-unpacked <= nc-download-unpacked
......
...@@ -92,7 +92,7 @@ class NextCloudTestCase(InstanceTestCase): ...@@ -92,7 +92,7 @@ class NextCloudTestCase(InstanceTestCase):
mail_smtpname="", mail_smtpname="",
cli_url="https://[%s]:9988/" % self.nextcloud_ipv6, cli_url="https://[%s]:9988/" % self.nextcloud_ipv6,
partition_dir=self.partition_dir, partition_dir=self.partition_dir,
trusted_domain_list=json.dumps(["[%s]:9988" % self.nextcloud_ipv6]), trusted_domain_list=json.dumps(["[%s]:9988" % self.nextcloud_ipv6] * 2),
trusted_proxy_list=[], trusted_proxy_list=[],
) )
data_dict.update(config_dict) data_dict.update(config_dict)
...@@ -309,8 +309,7 @@ class TestNextCloudParameters(NextCloudTestCase): ...@@ -309,8 +309,7 @@ class TestNextCloudParameters(NextCloudTestCase):
'instance.turn-server': 'turn.example.net:5439', 'instance.turn-server': 'turn.example.net:5439',
'instance.turn-secret': 'c4f0ead40a49bbbac3c58f7b9b43990f78ebd96900757ae67e10190a3a6b6053', 'instance.turn-secret': 'c4f0ead40a49bbbac3c58f7b9b43990f78ebd96900757ae67e10190a3a6b6053',
'instance.cli-url': 'nextcloud.example.com', 'instance.cli-url': 'nextcloud.example.com',
'instance.trusted-domain-1': 'nextcloud.example.com', 'instance.trusted-domain-list': 'nextcloud.example.com nextcloud.proxy.com',
'instance.trusted-domain-2': 'nextcloud.proxy.com',
'instance.trusted-proxy-list': '2001:67c:1254:e:89::5df3 127.0.0.1 10.23.1.3', 'instance.trusted-proxy-list': '2001:67c:1254:e:89::5df3 127.0.0.1 10.23.1.3',
} }
...@@ -345,6 +344,7 @@ class TestNextCloudParameters(NextCloudTestCase): ...@@ -345,6 +344,7 @@ class TestNextCloudParameters(NextCloudTestCase):
cli_url="nextcloud.example.com", cli_url="nextcloud.example.com",
partition_dir=self.partition_dir, partition_dir=self.partition_dir,
trusted_domain_list=json.dumps([ trusted_domain_list=json.dumps([
"[%s]:9988" % self.nextcloud_ipv6,
"[%s]:9988" % self.nextcloud_ipv6, "[%s]:9988" % self.nextcloud_ipv6,
"nextcloud.example.com", "nextcloud.example.com",
"nextcloud.proxy.com" "nextcloud.proxy.com"
......
...@@ -16,35 +16,35 @@ ...@@ -16,35 +16,35 @@
[template] [template]
filename = instance.cfg filename = instance.cfg
md5sum = a9e416eaa3ad7d2ea29cb90ce2c41a60 md5sum = 12591aca22ddc61f9fb1ec7e596873e3
[slaplte.jinja2] [slaplte.jinja2]
_update_hash_filename_ = slaplte.jinja2 _update_hash_filename_ = slaplte.jinja2
md5sum = c31dffa87765d93327f18ffd89ce36ca md5sum = c31dffa87765d93327f18ffd89ce36ca
[amarisoft-stats.jinja2.py] [ru_amarisoft-stats.jinja2.py]
_update_hash_filename_ = amarisoft-stats.jinja2.py _update_hash_filename_ = ru/amarisoft-stats.jinja2.py
md5sum = c4d5e9fcf460d88bc2b4bcfbdfe554f7 md5sum = c4d5e9fcf460d88bc2b4bcfbdfe554f7
[amarisoft-rf-info.jinja2.py] [ru_amarisoft-rf-info.jinja2.py]
_update_hash_filename_ = amarisoft-rf-info.jinja2.py _update_hash_filename_ = ru/amarisoft-rf-info.jinja2.py
md5sum = ab666fdfadbfc7d8a16ace38d295c883 md5sum = ab666fdfadbfc7d8a16ace38d295c883
[ru_libinstance.jinja2.cfg] [ru_libinstance.jinja2.cfg]
_update_hash_filename_ = ru/libinstance.jinja2.cfg _update_hash_filename_ = ru/libinstance.jinja2.cfg
md5sum = 6febf4dc601ba5feb30aa402f37265cf md5sum = d3ae8839b1a7b7a225bc69c3910c0b35
[ru_sdr_libinstance.jinja2.cfg] [ru_sdr_libinstance.jinja2.cfg]
_update_hash_filename_ = ru/sdr/libinstance.jinja2.cfg _update_hash_filename_ = ru/sdr/libinstance.jinja2.cfg
md5sum = c20b620111a4dc4bc2bcae57c2007cbe md5sum = de71c63b8df940207409de7e948f7c8c
[ru_lopcomm_libinstance.jinja2.cfg] [ru_lopcomm_libinstance.jinja2.cfg]
_update_hash_filename_ = ru/lopcomm/libinstance.jinja2.cfg _update_hash_filename_ = ru/lopcomm/libinstance.jinja2.cfg
md5sum = abce2deca15b8d7a8c5378e0789f8ce7 md5sum = b2af1e70141216a4db07cca655aa63a7
[ru_sunwave_libinstance.jinja2.cfg] [ru_sunwave_libinstance.jinja2.cfg]
_update_hash_filename_ = ru/sunwave/libinstance.jinja2.cfg _update_hash_filename_ = ru/sunwave/libinstance.jinja2.cfg
md5sum = 0450e9fa50844e4d6e51d608625c57f6 md5sum = c855ee7a6132899eb53b8d80ec27701a
[ru_lopcomm_ncclient_common.py] [ru_lopcomm_ncclient_common.py]
_update_hash_filename_ = ru/lopcomm/ncclient_common.py _update_hash_filename_ = ru/lopcomm/ncclient_common.py
...@@ -80,11 +80,11 @@ md5sum = 52da9fe3a569199e35ad89ae1a44c30e ...@@ -80,11 +80,11 @@ md5sum = 52da9fe3a569199e35ad89ae1a44c30e
[template-enb] [template-enb]
_update_hash_filename_ = instance-enb.jinja2.cfg _update_hash_filename_ = instance-enb.jinja2.cfg
md5sum = 3b380ac8a44aafc30cc6d87b35860fd6 md5sum = ae49a3a9a97407f9aea30981403ee1a2
[template-gnb] [template-gnb]
_update_hash_filename_ = instance-gnb.jinja2.cfg _update_hash_filename_ = instance-gnb.jinja2.cfg
md5sum = e8e87a50b861d733894eb69e1aefa683 md5sum = 54a0c7c3a2a1c905a15c58c650ee1095
[template-core-network] [template-core-network]
_update_hash_filename_ = instance-core-network.jinja2.cfg _update_hash_filename_ = instance-core-network.jinja2.cfg
...@@ -108,7 +108,7 @@ md5sum = dcaac06553a3222b14c0013a13f4a149 ...@@ -108,7 +108,7 @@ md5sum = dcaac06553a3222b14c0013a13f4a149
[enb.jinja2.cfg] [enb.jinja2.cfg]
filename = config/enb.jinja2.cfg filename = config/enb.jinja2.cfg
md5sum = a961cc1469bd2534645470f914f12905 md5sum = 914d781af63f4214e6cc3be4ffe93215
[drb_lte.jinja2.cfg] [drb_lte.jinja2.cfg]
filename = config/drb_lte.jinja2.cfg filename = config/drb_lte.jinja2.cfg
...@@ -122,10 +122,6 @@ md5sum = 84d3cef8fc7f1c2aed7c348d500f5636 ...@@ -122,10 +122,6 @@ md5sum = 84d3cef8fc7f1c2aed7c348d500f5636
filename = config/sib23.jinja2.asn filename = config/sib23.jinja2.asn
md5sum = a1973ba6e43d40e510d61d461c2d13ac md5sum = a1973ba6e43d40e510d61d461c2d13ac
[gnb.jinja2.cfg]
filename = config/gnb.jinja2.cfg
md5sum = a4f91c1c9cfd91d000f4845a88cdb38a
[mme.jinja2.cfg] [mme.jinja2.cfg]
filename = config/mme.jinja2.cfg filename = config/mme.jinja2.cfg
md5sum = 3d7833ddba3242cedcd74c7db52390c6 md5sum = 3d7833ddba3242cedcd74c7db52390c6
...@@ -136,7 +132,7 @@ md5sum = f167b4be5e327b276b42267e0678f577 ...@@ -136,7 +132,7 @@ md5sum = f167b4be5e327b276b42267e0678f577
[ru_dnsmasq.jinja2.cfg] [ru_dnsmasq.jinja2.cfg]
_update_hash_filename_ = ru/dnsmasq.jinja2.cfg _update_hash_filename_ = ru/dnsmasq.jinja2.cfg
md5sum = 345e4967d468b00c13d77821bce8a248 md5sum = 9bd5b08f23640f71ad109d186d060f2d
[ims.jinja2.cfg] [ims.jinja2.cfg]
filename = config/ims.jinja2.cfg filename = config/ims.jinja2.cfg
......
Changelog Changelog
========= =========
Version 1.0.344 (2023-11-03)
-------------
* Set dpc_snr_target to 25 for PUSCH also
Version 1.0.341 (2023-10-20)
-------------
* Publish amarisoft version and license expiration information
* Add network name parameter
Version 1.0.340 (2023-10-20)
-------------
* Update RRH firmware and reset
Version 1.0.339 (2023-10-16) Version 1.0.339 (2023-10-16)
------------- -------------
* Lopcomm firmware update * Lopcomm firmware update
* RRH reset (reboot) function added * RRH reset (reboot) function added
* Fix cpri_tx_dbm parameter * Fix cpri_tx_dbm parameter
......
This diff is collapsed.
...@@ -6,16 +6,8 @@ parts = ...@@ -6,16 +6,8 @@ parts =
xamari-xlog-service xamari-xlog-service
{% if slapparameter_dict.get('xlog_fluentbit_forward_host') %} {% if slapparameter_dict.get('xlog_fluentbit_forward_host') %}
xlog-fluentbit-service xlog-fluentbit-service
{% endif %}
amarisoft-stats-service
amarisoft-rf-info-service
{% if ru == "lopcomm" %}
sshd-service
sshd-add-authorized-key
sshd-promise
{% endif %} {% endif %}
check-baseband-latency.py check-baseband-latency.py
check-amarisoft-stats-log.py
monitor-base monitor-base
publish-connection-information publish-connection-information
...@@ -179,111 +171,6 @@ wrapper-path = ${directory:service}/${:_buildout_section_name_} ...@@ -179,111 +171,6 @@ wrapper-path = ${directory:service}/${:_buildout_section_name_}
hash-files = ${:fluentbit-config} hash-files = ${:fluentbit-config}
{% endif %} {% endif %}
[amarisoft-stats-template]
recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
log-output = ${directory:var}/log/amarisoft-stats.json.log
context =
section directory directory
key slapparameter_dict slap-configuration:configuration
key log_file :log-output
raw stats_period {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
raw testing {{ slapparameter_dict.get("testing", False) }}
raw python_path {{ buildout_directory}}/bin/pythonwitheggs
mode = 0775
url = {{ amarisoft_stats_template }}
output = ${directory:bin}/amarisoft-stats.py
[amarisoft-rf-info-template]
recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
log-output = ${directory:var}/log/amarisoft-rf-info.json.log
context =
section directory directory
key slapparameter_dict slap-configuration:configuration
key log_file :log-output
raw stats_period {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
raw testing {{ slapparameter_dict.get("testing", False) }}
raw python_path {{ buildout_directory}}/bin/pythonwitheggs
mode = 0775
url = {{ amarisoft_rf_info_template }}
output = ${directory:bin}/amarisoft-rf-info.py
[amarisoft-stats-service]
recipe = slapos.cookbook:wrapper
command-line = ${amarisoft-stats-template:output}
wrapper-path = ${directory:service}/amarisoft-stats
mode = 0775
hash-files =
${amarisoft-stats-template:output}
[amarisoft-rf-info-service]
recipe = slapos.cookbook:wrapper
command-line = ${amarisoft-rf-info-template:output}
wrapper-path = ${directory:service}/amarisoft-rf-info
mode = 0775
hash-files =
${amarisoft-rf-info-template:output}
[user-info]
recipe = slapos.cookbook:userinfo
# Deploy openssh-server
[sshd-port]
recipe = slapos.cookbook:free_port
minimum = 22222
maximum = 22231
ip = ${slap-configuration:ipv6-random}
[sshd-config]
recipe = slapos.recipe.template:jinja2
output = ${directory:etc}/sshd.conf
path_pid = ${directory:run}/sshd.pid
inline =
PidFile ${:path_pid}
Port ${sshd-port:port}
ListenAddress ${slap-configuration:ipv6-random}
Protocol 2
HostKey ${sshd-ssh-host-rsa-key:output}
HostKey ${sshd-ssh-host-ecdsa-key:output}
PasswordAuthentication no
PubkeyAuthentication yes
HostKeyAlgorithms ssh-rsa,ssh-dss,rsa-sha2-512,rsa-sha2-256,ecdsa-sha2-nistp521
AuthorizedKeysFile ${buildout:directory}/.ssh/authorized_keys
Subsystem sftp {{ openssh_location }}/libexec/sftp-server
[sshd-service]
recipe = slapos.cookbook:wrapper
command-line = {{ openssh_location }}/sbin/sshd -D -e -f ${sshd-config:output}
wrapper-path = ${directory:service}/sshd
hash-files = ${sshd-config:output}
environment =
HOME=${directory:home}
[sshd-add-authorized-key]
recipe = slapos.cookbook:dropbear.add_authorized_key
home = ${buildout:directory}
key = {{ slapparameter_dict.get("user-authorized-key", '') }}
[sshd-ssh-keygen-base]
recipe = plone.recipe.command
output = ${directory:etc}/${:_buildout_section_name_}
command = {{ openssh_output_keygen }} -f ${:output} -N '' ${:extra-args}
[sshd-ssh-host-rsa-key]
<=sshd-ssh-keygen-base
extra-args=-t rsa
[sshd-ssh-host-ecdsa-key]
<=sshd-ssh-keygen-base
extra-args=-t ecdsa -b 521
[sshd-promise]
<= monitor-promise-base
promise = check_socket_listening
name = sshd.py
config-host = ${slap-configuration:ipv6-random}
config-port = ${sshd-port:port}
[config-base] [config-base]
recipe = slapos.recipe.template:jinja2 recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do extensions = jinja2.ext.do
...@@ -303,6 +190,8 @@ context = ...@@ -303,6 +190,8 @@ context =
raw trx {{ trx }} raw trx {{ trx }}
raw bbu {{ bbu }} raw bbu {{ bbu }}
raw ru {{ ru }} raw ru {{ ru }}
json do_lte true
json do_nr false
import netaddr netaddr import netaddr netaddr
${:extra-context} ${:extra-context}
...@@ -351,12 +240,6 @@ current-earfcn = {{ ors_version['current-earfcn'] }} ...@@ -351,12 +240,6 @@ current-earfcn = {{ ors_version['current-earfcn'] }}
amarisoft-version = {{ lte_version }} amarisoft-version = {{ lte_version }}
license-expiration = {{ lte_expiration }} license-expiration = {{ lte_expiration }}
monitor-gadget-url = ${:monitor-base-url}/gadget/software.cfg.html monitor-gadget-url = ${:monitor-base-url}/gadget/software.cfg.html
{% if ru == "lopcomm" %}
ssh-command = ssh ${user-info:pw-name}@${slap-configuration:ipv6-random} -p ${sshd-port:port}
ssh-url = ssh://${user-info:pw-name}@[${slap-configuration:ipv6-random}]:${sshd-port:port}
ru-firmware = {{ru_lopcomm_firmware_filename}}
ru-ipv6 = ${slap-configuration:tap-ipv6-gateway}
{% endif %}
[monitor-instance-parameter] [monitor-instance-parameter]
{% if slapparameter_dict.get("name", None) %} {% if slapparameter_dict.get("name", None) %}
...@@ -374,14 +257,6 @@ name = ${:_buildout_section_name_} ...@@ -374,14 +257,6 @@ name = ${:_buildout_section_name_}
<= macro.promise <= macro.promise
promise = check_baseband_latency promise = check_baseband_latency
config-testing = {{ slapparameter_dict.get("testing", False) }} config-testing = {{ slapparameter_dict.get("testing", False) }}
config-amarisoft-stats-log = ${amarisoft-stats-template:log-output} config-amarisoft-stats-log = ${ru_amarisoft-stats-template:log-output}
config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }} config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
config-min-rxtx-delay = {{ slapparameter_dict.get("min_rxtx_delay", 0) }} config-min-rxtx-delay = {{ slapparameter_dict.get("min_rxtx_delay", 0) }}
[check-amarisoft-stats-log.py]
<= macro.promise
promise = check_amarisoft_stats_log
output = ${directory:plugins}/check-amarisoft-stats-log.py
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-amarisoft-stats-log = ${amarisoft-stats-template:log-output}
config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
...@@ -188,7 +188,7 @@ context = ...@@ -188,7 +188,7 @@ context =
raw testing {{ slapparameter_dict.get("testing", False) }} raw testing {{ slapparameter_dict.get("testing", False) }}
raw python_path {{ buildout_directory}}/bin/pythonwitheggs raw python_path {{ buildout_directory}}/bin/pythonwitheggs
mode = 0775 mode = 0775
url = {{ amarisoft_stats_template }} url = {{ ru_amarisoft_stats_template }}
output = ${directory:bin}/amarisoft-stats.py output = ${directory:bin}/amarisoft-stats.py
[amarisoft-rf-info-template] [amarisoft-rf-info-template]
...@@ -203,7 +203,7 @@ context = ...@@ -203,7 +203,7 @@ context =
raw testing {{ slapparameter_dict.get("testing", False) }} raw testing {{ slapparameter_dict.get("testing", False) }}
raw python_path {{ buildout_directory}}/bin/pythonwitheggs raw python_path {{ buildout_directory}}/bin/pythonwitheggs
mode = 0775 mode = 0775
url = {{ amarisoft_rf_info_template }} url = {{ ru_amarisoft_rf_info_template }}
output = ${directory:bin}/amarisoft-rf-info.py output = ${directory:bin}/amarisoft-rf-info.py
[amarisoft-stats-service] [amarisoft-stats-service]
...@@ -242,6 +242,8 @@ context = ...@@ -242,6 +242,8 @@ context =
raw trx {{ trx }} raw trx {{ trx }}
raw bbu {{ bbu }} raw bbu {{ bbu }}
raw ru {{ ru }} raw ru {{ ru }}
json do_lte false
json do_nr true
import netaddr netaddr import netaddr netaddr
${:extra-context} ${:extra-context}
...@@ -255,11 +257,13 @@ output = ${directory:etc}/drb.cfg ...@@ -255,11 +257,13 @@ output = ${directory:etc}/drb.cfg
{% if slapparameter_dict.get("gnb_config_link", None) %} {% if slapparameter_dict.get("gnb_config_link", None) %}
url = ${gnb-config-dl:target} url = ${gnb-config-dl:target}
{% else %} {% else %}
url = {{ gnb_template }} url = {{ enb_template }}
{% endif %} {% endif %}
output = ${directory:etc}/gnb.cfg output = ${directory:etc}/gnb.cfg
extra-context = extra-context =
key drb_file drb-config:output key drb_file drb-config:output
import-list =
rawfile slaplte.jinja2 {{ slaplte_template }}
[publish-connection-information] [publish-connection-information]
<= monitor-publish <= monitor-publish
......
...@@ -279,8 +279,8 @@ extra-context = ...@@ -279,8 +279,8 @@ extra-context =
raw slaplte_template ${slaplte.jinja2:target} raw slaplte_template ${slaplte.jinja2:target}
raw drb_lte_template ${drb_lte.jinja2.cfg:target} raw drb_lte_template ${drb_lte.jinja2.cfg:target}
raw sib23_template ${sib23.jinja2.asn:target} raw sib23_template ${sib23.jinja2.asn:target}
raw amarisoft_stats_template ${amarisoft-stats.jinja2.py:target} raw ru_amarisoft_stats_template ${ru_amarisoft-stats.jinja2.py:target}
raw amarisoft_rf_info_template ${amarisoft-rf-info.jinja2.py:target} raw ru_amarisoft_rf_info_template ${ru_amarisoft-rf-info.jinja2.py:target}
raw ru_lopcomm_stats_template ${ru_lopcomm_stats.jinja2.py:target} raw ru_lopcomm_stats_template ${ru_lopcomm_stats.jinja2.py:target}
raw ru_lopcomm_config_template ${ru_lopcomm_config.jinja2.py:target} raw ru_lopcomm_config_template ${ru_lopcomm_config.jinja2.py:target}
raw ru_lopcomm_software_template ${ru_lopcomm_software.jinja2.py:target} raw ru_lopcomm_software_template ${ru_lopcomm_software.jinja2.py:target}
...@@ -316,10 +316,11 @@ extra-context = ...@@ -316,10 +316,11 @@ extra-context =
key lte_expiration amarisoft:lte-expiration key lte_expiration amarisoft:lte-expiration
key enb amarisoft:enb key enb amarisoft:enb
key sdr amarisoft:sdr key sdr amarisoft:sdr
raw gnb_template ${gnb.jinja2.cfg:target} raw enb_template ${enb.jinja2.cfg:target}
raw slaplte_template ${slaplte.jinja2:target}
raw drb_nr_template ${drb_nr.jinja2.cfg:target} raw drb_nr_template ${drb_nr.jinja2.cfg:target}
raw amarisoft_stats_template ${amarisoft-stats.jinja2.py:target} raw ru_amarisoft_stats_template ${ru_amarisoft-stats.jinja2.py:target}
raw amarisoft_rf_info_template ${amarisoft-rf-info.jinja2.py:target} raw ru_amarisoft_rf_info_template ${ru_amarisoft-rf-info.jinja2.py:target}
raw openssl_location ${openssl:location} raw openssl_location ${openssl:location}
raw default_nr_bandwidth ${default-params:default-nr-bandwidth} raw default_nr_bandwidth ${default-params:default-nr-bandwidth}
raw default_nr_ssb_pos_bitmap ${default-params:default-nr-ssb-pos-bitmap} raw default_nr_ssb_pos_bitmap ${default-params:default-nr-ssb-pos-bitmap}
......
...@@ -33,3 +33,9 @@ stop-on-error = true ...@@ -33,3 +33,9 @@ stop-on-error = true
[setcap-netcapdo] [setcap-netcapdo]
<= setcap <= setcap
exe = ${netcapdo:exe} exe = ${netcapdo:exe}
[ru_amarisoft-stats.jinja2.py]
<= download-base
[ru_amarisoft-rf-info.jinja2.py]
<= download-base
...@@ -7,7 +7,8 @@ port=5354 ...@@ -7,7 +7,8 @@ port=5354
{%- set plen = netaddr.IPNetwork(vtap.network).prefixlen %} {%- set plen = netaddr.IPNetwork(vtap.network).prefixlen %}
# {{ cell_ref }} @ {{ ru_tap }} # {{ cell_ref }} @ {{ ru_tap }}
dhcp-range=tag:{{ ru_tap }},{{ vtap.gateway }},{{ vtap.gateway }},static,{{ plen }},5m {#- TODO consider using /128 as we give only 1 address to RU #}
dhcp-range=tag:{{ ru_tap }},{{ vtap.gateway }},{{ vtap.gateway }},static,{{ max(plen,64) }},5m
dhcp-host={{ cell.ru_mac_addr }},tag:{{ ru_tap }},[{{ vtap.gateway }}] dhcp-host={{ cell.ru_mac_addr }},tag:{{ ru_tap }},[{{ vtap.gateway }}]
# option 17 used for RU callhome # option 17 used for RU callhome
# dhcp-option=option6:17,[{{ vtap.addr }}] # dhcp-option=option6:17,[{{ vtap.addr }}]
......
...@@ -35,5 +35,5 @@ destination = ${buildout:directory}/ncclient_common.py ...@@ -35,5 +35,5 @@ destination = ${buildout:directory}/ncclient_common.py
[ru_lopcomm_firmware-dl] [ru_lopcomm_firmware-dl]
recipe = slapos.recipe.build:download recipe = slapos.recipe.build:download
url = https://lab.nexedi.com/nexedi/ors-utils/raw/master/lopcomm-firmware/${:filename} url = https://lab.nexedi.com/nexedi/ors-utils/raw/master/lopcomm-firmware/${:filename}
filename = PR.PRM61C70V1004.006.tar.gz filename = PR.PRM61C70V1005.004.tar.gz
md5sum = 5139019aae77c2f3178a99915f1c57dc md5sum = f16413604a8c7631fc6e3782fa9a2695
...@@ -3,3 +3,7 @@ ...@@ -3,3 +3,7 @@
{%- macro buildout_ru(ru_ref, cell) %} {%- macro buildout_ru(ru_ref, cell) %}
{#- nothing SDR-specific #} {#- nothing SDR-specific #}
{%- endmacro %} {%- endmacro %}
{%- macro buildout() %}
{#- nothing SDR-specific #}
{%- endmacro %}
...@@ -3,3 +3,7 @@ ...@@ -3,3 +3,7 @@
{%- macro buildout_ru(ru_ref, cell) %} {%- macro buildout_ru(ru_ref, cell) %}
{#- nothing SunWave-specific #} {#- nothing SunWave-specific #}
{%- endmacro %} {%- endmacro %}
{%- macro buildout() %}
{#- nothing SunWave-specific #}
{%- endmacro %}
...@@ -26,7 +26,6 @@ parts += ...@@ -26,7 +26,6 @@ parts +=
dnsmasq-core-network.jinja2.cfg dnsmasq-core-network.jinja2.cfg
ims.jinja2.cfg ims.jinja2.cfg
enb.jinja2.cfg enb.jinja2.cfg
gnb.jinja2.cfg
ue_db.jinja2.cfg ue_db.jinja2.cfg
ue-lte.jinja2.cfg ue-lte.jinja2.cfg
ue-nr.jinja2.cfg ue-nr.jinja2.cfg
...@@ -58,12 +57,6 @@ output = ${buildout:directory}/template.cfg ...@@ -58,12 +57,6 @@ output = ${buildout:directory}/template.cfg
recipe = slapos.recipe.build:download recipe = slapos.recipe.build:download
url = ${:_profile_base_location_}/${:_update_hash_filename_} url = ${:_profile_base_location_}/${:_update_hash_filename_}
[amarisoft-stats.jinja2.py]
<= download-base
[amarisoft-rf-info.jinja2.py]
<= download-base
[template-enb] [template-enb]
<= download-base <= download-base
...@@ -124,9 +117,6 @@ filename = enb.jinja2.cfg ...@@ -124,9 +117,6 @@ filename = enb.jinja2.cfg
[sib23.jinja2.asn] [sib23.jinja2.asn]
<= copy-config-to-instance <= copy-config-to-instance
filename = sib23.jinja2.asn filename = sib23.jinja2.asn
[gnb.jinja2.cfg]
<= copy-config-to-instance
filename = gnb.jinja2.cfg
[ue_db.jinja2.cfg] [ue_db.jinja2.cfg]
<= copy-config-to-instance <= copy-config-to-instance
filename = ue_db.jinja2.cfg filename = ue_db.jinja2.cfg
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment