From 9f080096814b3ee2fa812cb1efc197a1c24e1312 Mon Sep 17 00:00:00 2001 From: Michael Bayer Date: Mon, 12 Aug 2024 02:04:10 -0400 Subject: [PATCH] Add Valkey support (#175) This is a copy of redis with names changed for Valkey. --- README.rst | 2 + pifpaf/drivers/valkey.py | 90 ++++++++++++++++++++++++++++++++++++ pifpaf/tests/test_drivers.py | 47 +++++++++++++++++++ setup.cfg | 1 + 4 files changed, 140 insertions(+) create mode 100644 pifpaf/drivers/valkey.py diff --git a/README.rst b/README.rst index 39b2d16..e9cee1f 100644 --- a/README.rst +++ b/README.rst @@ -24,6 +24,7 @@ Pifpaf currently supports: * `InfluxDB`_ * `Etcd`_ (with clustering) * `Redis`_ (with sentinel mode) +* `Valkey`_ * `Elasticsearch`_ * `ZooKeeper`_ * `Gnocchi`_ @@ -47,6 +48,7 @@ Pifpaf currently supports: .. _InfluxDB: http://influxdb.org .. _Etcd: https://coreos.com/etcd/ .. _Redis: http://redis.io/ +.. _Valkey: https://valkey.io/ .. _Elasticsearch: https://www.elastic.co/ .. _ZooKeeper: https://zookeeper.apache.org/ .. _Gnocchi: http://gnocchi.xyz diff --git a/pifpaf/drivers/valkey.py b/pifpaf/drivers/valkey.py new file mode 100644 index 0000000..07f7511 --- /dev/null +++ b/pifpaf/drivers/valkey.py @@ -0,0 +1,90 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +from pifpaf import drivers + + +class ValkeyDriver(drivers.Driver): + + DEFAULT_PORT = 6379 + DEFAULT_PORT_SENTINEL = 6380 + DEFAULT_PASSWORD = '' + + def __init__(self, port=DEFAULT_PORT, + sentinel=False, sentinel_port=DEFAULT_PORT_SENTINEL, + password=DEFAULT_PASSWORD, **kwargs): + """Create a new Valkey server.""" + super(ValkeyDriver, self).__init__(**kwargs) + self.port = port + self.sentinel = sentinel + self.sentinel_port = sentinel_port + self.password = password + + @classmethod + def get_options(cls): + return [ + {"param_decls": ["--port"], + "type": int, + "default": cls.DEFAULT_PORT, + "help": "port to use for Valkey"}, + {"param_decls": ["--sentinel"], + "is_flag": True, + "help": "activate Valkey sentinel"}, + {"param_decls": ["--sentinel-port"], + "type": int, + "default": cls.DEFAULT_PORT_SENTINEL, + "help": "port to use for Valkey sentinel"}, + {"param_decls": ["--password"], + "default": cls.DEFAULT_PASSWORD, + "help": "Valkey and Valkey sentinel password"}, + ] + + def _setUp(self): + super(ValkeyDriver, self)._setUp() + valkey_conf = """dir %s +port %d +""" % (self.tempdir, self.port) + if self.password: + valkey_conf += "requirepass %s\n" % self.password + c, _ = self._exec( + ["valkey-server", "-"], + stdin=(valkey_conf).encode('ascii'), + wait_for_line="eady to accept connections") + + if self.sentinel: + cfg = os.path.join(self.tempdir, "valkey-sentinel.conf") + sentinel_conf = """dir %s +port %d +sentinel monitor pifpaf localhost %d 1 +""" % (self.tempdir, self.sentinel_port, self.port) + if self.password: + sentinel_conf += ( + "sentinel auth-pass pifpaf %s\n" % self.password) + sentinel_conf += "requirepass %s\n" % self.password + with open(cfg, "w") as f: + f.write(sentinel_conf) + + c, _ = self._exec( + ["valkey-sentinel", cfg], + wait_for_line=r"# Sentinel (runid|ID) is") + + self.addCleanup(self._kill, c) + + self.putenv("VALKEY_SENTINEL_PORT", + str(self.sentinel_port)) + + self.putenv("VALKEY_PORT", str(self.port)) + self.url = "valkey://localhost:%d" % self.port + self.putenv("URL", self.url) diff --git a/pifpaf/tests/test_drivers.py b/pifpaf/tests/test_drivers.py index 80a159c..9dcf8d6 100644 --- a/pifpaf/tests/test_drivers.py +++ b/pifpaf/tests/test_drivers.py @@ -49,6 +49,7 @@ from pifpaf.drivers import redis from pifpaf.drivers import s3rver from pifpaf.drivers import swift +from pifpaf.drivers import valkey from pifpaf.drivers import vault from pifpaf.drivers import zookeeper @@ -309,6 +310,52 @@ def test_redis_sentinel_with_password(self): f.sentinel_port) self._run("redis-cli -p %d -a secrete llen pifpaf" % f.port) + @testtools.skipUnless(shutil.which("valkey-server"), + "valkey-server not found") + def test_valkey(self): + port = 6384 + f = self.useFixture(valkey.ValkeyDriver(port=port)) + self.assertEqual("valkey://localhost:%d" % port, + os.getenv("PIFPAF_URL")) + self.assertEqual(str(port), os.getenv("PIFPAF_VALKEY_PORT")) + self._run("valkey-cli -p %d llen pifpaf" % f.port) + + @testtools.skipUnless(shutil.which("valkey-server"), + "valkey-server not found") + def test_valkey_with_password(self): + port = 6384 + f = self.useFixture(valkey.ValkeyDriver(port=port, password='secrete')) + self.assertEqual("valkey://localhost:%d" % port, + os.getenv("PIFPAF_URL")) + self.assertEqual(str(port), os.getenv("PIFPAF_VALKEY_PORT")) + self._run("valkey-cli -p %d -a secrete llen pifpaf" % f.port) + + @testtools.skipUnless(shutil.which("valkey-sentinel"), + "valkey-sentinel not found") + def test_valkey_sentinel(self): + port = 6385 + f = self.useFixture(valkey.ValkeyDriver(sentinel=True, port=port)) + self.assertEqual("valkey://localhost:%d" % port, + os.getenv("PIFPAF_URL")) + self.assertEqual(str(port), os.getenv("PIFPAF_VALKEY_PORT")) + self.assertEqual("6380", os.getenv("PIFPAF_VALKEY_SENTINEL_PORT")) + self._run("valkey-cli -p %d sentinel master pifpaf" % f.sentinel_port) + self._run("valkey-cli -p %d llen pifpaf" % f.port) + + @testtools.skipUnless(shutil.which("valkey-sentinel"), + "valkey-sentinel not found") + def test_valkey_sentinel_with_password(self): + port = 6385 + f = self.useFixture(valkey.ValkeyDriver(sentinel=True, port=port, + password='secrete')) + self.assertEqual("valkey://localhost:%d" % port, + os.getenv("PIFPAF_URL")) + self.assertEqual(str(port), os.getenv("PIFPAF_VALKEY_PORT")) + self.assertEqual("6380", os.getenv("PIFPAF_VALKEY_SENTINEL_PORT")) + self._run("valkey-cli -p %d -a secrete sentinel master pifpaf" % + f.sentinel_port) + self._run("valkey-cli -p %d -a secrete llen pifpaf" % f.port) + @testtools.skipUnless(shutil.which( "zkServer.sh", path=":".join(zookeeper.ZooKeeperDriver.PATH)), "ZooKeeper not found") diff --git a/setup.cfg b/setup.cfg index 0818a22..c19fc1a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -66,6 +66,7 @@ pifpaf.daemons = qdrouterd = pifpaf.drivers.qdrouterd:QdrouterdDriver rabbitmq = pifpaf.drivers.rabbitmq:RabbitMQDriver redis = pifpaf.drivers.redis:RedisDriver + valkey = pifpaf.drivers.valkey:ValkeyDriver s3rver = pifpaf.drivers.s3rver:S3rverDriver zookeeper = pifpaf.drivers.zookeeper:ZooKeeperDriver vault = pifpaf.drivers.vault:VaultDriver