Feature/persist config (#370)

* Cassandra tables for config

* Config is backed by Cassandra
This commit is contained in:
cybermaggedon 2025-05-07 12:58:32 +01:00 committed by GitHub
parent f7123ac57f
commit 4461d7b289
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 516 additions and 103 deletions

View file

@ -2,56 +2,93 @@
from trustgraph.schema import ConfigResponse
from trustgraph.schema import ConfigValue, Error
# This behaves just like a dict, should be easier to add persistent storage
# later
class ConfigurationItems(dict):
pass
from ... tables.config import ConfigTableStore
class Configuration(dict):
class ConfigurationClass:
async def keys(self):
return await self.table_store.get_keys(self.type)
async def values(self):
vals = await self.table_store.get_values(self.type)
return {
v[0]: v[1]
for v in vals
}
async def get(self, key):
return await self.table_store.get_value(self.type, key)
async def put(self, key, value):
return await self.table_store.put_config(self.type, key, value)
async def delete(self, key):
return await self.table_store.delete_key(self.type, key)
async def has(self, key):
val = await self.table_store.get_value(self.type, key)
return val is not None
class Configuration:
# FIXME: The state is held internally. This only works if there's
# one config service. Should be more than one, and use a
# back-end state store.
def __init__(self, push):
# FIXME: This has state now, but does it address all of the above?
# REVIEW: Above
# Version counter
self.version = 0
# FIXME: Some version vs config race conditions
def __init__(self, push, host, user, password, keyspace):
# External function to respond to update
self.push = push
def __getitem__(self, key):
if key not in self:
self[key] = ConfigurationItems()
return dict.__getitem__(self, key)
self.table_store = ConfigTableStore(
host, user, password, keyspace
)
async def inc_version(self):
await self.table_store.inc_version()
async def get_version(self):
return await self.table_store.get_version()
def get(self, type):
c = ConfigurationClass()
c.table_store = self.table_store
c.type = type
return c
async def handle_get(self, v):
for k in v.keys:
if k.type not in self or k.key not in self[k.type]:
return ConfigResponse(
version = None,
values = None,
directory = None,
config = None,
error = Error(
type = "key-error",
message = f"Key error"
)
)
# for k in v.keys:
# if k.type not in self or k.key not in self[k.type]:
# return ConfigResponse(
# version = None,
# values = None,
# directory = None,
# config = None,
# error = Error(
# type = "key-error",
# message = f"Key error"
# )
# )
values = [
ConfigValue(
type = k.type,
key = k.key,
value = self[k.type][k.key]
value = await self.table_store.get_value(k.type, k.key)
)
for k in v.keys
]
return ConfigResponse(
version = self.version,
version = await self.get_version(),
values = values,
directory = None,
config = None,
@ -60,23 +97,23 @@ class Configuration(dict):
async def handle_list(self, v):
if v.type not in self:
# if v.type not in self:
return ConfigResponse(
version = None,
values = None,
directory = None,
config = None,
error = Error(
type = "key-error",
message = "No such type",
),
)
# return ConfigResponse(
# version = None,
# values = None,
# directory = None,
# config = None,
# error = Error(
# type = "key-error",
# message = "No such type",
# ),
# )
return ConfigResponse(
version = self.version,
version = await self.get_version(),
values = None,
directory = list(self[v.type].keys()),
directory = await self.table_store.get_keys(v.type),
config = None,
error = None,
)
@ -96,17 +133,17 @@ class Configuration(dict):
)
)
values = [
ConfigValue(
type = v.type,
key = k,
value = self[v.type][k],
)
for k in self[v.type]
]
v = await self.table_store.get_values(v.type)
values = map(
lambda x: ConfigValue(
type = v.type, key = x[0], value = x[1]
),
v
)
return ConfigResponse(
version = self.version,
version = await self.get_version(),
values = values,
directory = None,
config = None,
@ -115,23 +152,24 @@ class Configuration(dict):
async def handle_delete(self, v):
for k in v.keys:
if k.type not in self or k.key not in self[k.type]:
return ConfigResponse(
version = None,
values = None,
directory = None,
config = None,
error = Error(
type = "key-error",
message = f"Key error"
)
)
# for k in v.keys:
# if k.type not in self or k.key not in self[k.type]:
# return ConfigResponse(
# version = None,
# values = None,
# directory = None,
# config = None,
# error = Error(
# type = "key-error",
# message = f"Key error"
# )
# )
for k in v.keys:
del self[k.type][k.key]
self.version += 1
await self.table_store.delete_key(k.type, k.key)
await self.inc_version()
await self.push()
@ -147,9 +185,10 @@ class Configuration(dict):
async def handle_put(self, v):
for k in v.values:
self[k.type][k.key] = k.value
self.version += 1
await self.table_store.put_config(k.type, k.key, k.value)
await self.inc_version()
await self.push()
@ -161,14 +200,29 @@ class Configuration(dict):
error = None,
)
async def get_config(self):
table = await self.table_store.get_all()
config = {}
for row in table:
if row[0] not in config:
config[row[0]] = {}
config[row[0]][row[1]] = row[2]
return config
async def handle_config(self, v):
config = await self.get_config()
return ConfigResponse(
version = self.version,
version = await self.get_version(),
value = None,
directory = None,
values = None,
config = self,
config = config,
error = None,
)