blueprints: improve file change handler (#7813)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens L 2023-12-08 16:04:00 +02:00 committed by GitHub
parent fef1090a4e
commit a9dba4eb5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 17 additions and 18 deletions

View File

@ -75,13 +75,13 @@ class BlueprintEventHandler(FileSystemEventHandler):
return return
if event.is_directory: if event.is_directory:
return return
if isinstance(event, FileCreatedEvent):
LOGGER.debug("new blueprint file created, starting discovery")
blueprints_discovery.delay()
if isinstance(event, FileModifiedEvent):
path = Path(event.src_path)
root = Path(CONFIG.get("blueprints_dir")).absolute() root = Path(CONFIG.get("blueprints_dir")).absolute()
path = Path(event.src_path).absolute()
rel_path = str(path.relative_to(root)) rel_path = str(path.relative_to(root))
if isinstance(event, FileCreatedEvent):
LOGGER.debug("new blueprint file created, starting discovery", path=rel_path)
blueprints_discovery.delay(rel_path)
if isinstance(event, FileModifiedEvent):
for instance in BlueprintInstance.objects.filter(path=rel_path, enabled=True): for instance in BlueprintInstance.objects.filter(path=rel_path, enabled=True):
LOGGER.debug("modified blueprint file, starting apply", instance=instance) LOGGER.debug("modified blueprint file, starting apply", instance=instance)
apply_blueprint.delay(instance.pk.hex) apply_blueprint.delay(instance.pk.hex)
@ -98,39 +98,32 @@ def blueprints_find_dict():
return blueprints return blueprints
def blueprints_find(): def blueprints_find() -> list[BlueprintFile]:
"""Find blueprints and return valid ones""" """Find blueprints and return valid ones"""
blueprints = [] blueprints = []
root = Path(CONFIG.get("blueprints_dir")) root = Path(CONFIG.get("blueprints_dir"))
for path in root.rglob("**/*.yaml"): for path in root.rglob("**/*.yaml"):
rel_path = path.relative_to(root)
# Check if any part in the path starts with a dot and assume a hidden file # Check if any part in the path starts with a dot and assume a hidden file
if any(part for part in path.parts if part.startswith(".")): if any(part for part in path.parts if part.startswith(".")):
continue continue
LOGGER.debug("found blueprint", path=str(path))
with open(path, "r", encoding="utf-8") as blueprint_file: with open(path, "r", encoding="utf-8") as blueprint_file:
try: try:
raw_blueprint = load(blueprint_file.read(), BlueprintLoader) raw_blueprint = load(blueprint_file.read(), BlueprintLoader)
except YAMLError as exc: except YAMLError as exc:
raw_blueprint = None raw_blueprint = None
LOGGER.warning("failed to parse blueprint", exc=exc, path=str(path)) LOGGER.warning("failed to parse blueprint", exc=exc, path=str(rel_path))
if not raw_blueprint: if not raw_blueprint:
continue continue
metadata = raw_blueprint.get("metadata", None) metadata = raw_blueprint.get("metadata", None)
version = raw_blueprint.get("version", 1) version = raw_blueprint.get("version", 1)
if version != 1: if version != 1:
LOGGER.warning("invalid blueprint version", version=version, path=str(path)) LOGGER.warning("invalid blueprint version", version=version, path=str(rel_path))
continue continue
file_hash = sha512(path.read_bytes()).hexdigest() file_hash = sha512(path.read_bytes()).hexdigest()
blueprint = BlueprintFile( blueprint = BlueprintFile(str(rel_path), version, file_hash, int(path.stat().st_mtime))
str(path.relative_to(root)), version, file_hash, int(path.stat().st_mtime)
)
blueprint.meta = from_dict(BlueprintMetadata, metadata) if metadata else None blueprint.meta = from_dict(BlueprintMetadata, metadata) if metadata else None
blueprints.append(blueprint) blueprints.append(blueprint)
LOGGER.debug(
"parsed & loaded blueprint",
hash=file_hash,
path=str(path),
)
return blueprints return blueprints
@ -138,10 +131,12 @@ def blueprints_find():
throws=(DatabaseError, ProgrammingError, InternalError), base=MonitoredTask, bind=True throws=(DatabaseError, ProgrammingError, InternalError), base=MonitoredTask, bind=True
) )
@prefill_task @prefill_task
def blueprints_discovery(self: MonitoredTask): def blueprints_discovery(self: MonitoredTask, path: Optional[str] = None):
"""Find blueprints and check if they need to be created in the database""" """Find blueprints and check if they need to be created in the database"""
count = 0 count = 0
for blueprint in blueprints_find(): for blueprint in blueprints_find():
if path and blueprint.path != path:
continue
check_blueprint_v1_file(blueprint) check_blueprint_v1_file(blueprint)
count += 1 count += 1
self.set_status( self.set_status(
@ -171,7 +166,11 @@ def check_blueprint_v1_file(blueprint: BlueprintFile):
metadata={}, metadata={},
) )
instance.save() instance.save()
LOGGER.info(
"Creating new blueprint instance from file", instance=instance, path=instance.path
)
if instance.last_applied_hash != blueprint.hash: if instance.last_applied_hash != blueprint.hash:
LOGGER.info("Applying blueprint due to changed file", instance=instance, path=instance.path)
apply_blueprint.delay(str(instance.pk)) apply_blueprint.delay(str(instance.pk))