Skip to content

Commit

Permalink
Merge pull request #994 from minrk/consolidate_lock
Browse files Browse the repository at this point in the history
consolidate lock file handling
  • Loading branch information
consideRatio committed Sep 16, 2024
2 parents f7118ed + 0c32277 commit 7be7eb4
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 67 deletions.
1 change: 0 additions & 1 deletion dev-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
filelock
packaging
pytest
pytest-cov
Expand Down
1 change: 0 additions & 1 deletion integration-tests/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
filelock
pytest
pytest-cov
pytest-asyncio
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"jinja2",
"pluggy==1.*",
"backoff",
"filelock",
"requests",
"bcrypt",
"jupyterhub-traefik-proxy==1.*",
Expand Down
107 changes: 45 additions & 62 deletions tljh/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
import sys
import time
from collections.abc import Mapping, Sequence
from contextlib import contextmanager
from copy import deepcopy

import requests
from filelock import FileLock, Timeout

from .yaml import yaml

Expand All @@ -32,6 +34,22 @@
CONFIG_FILE = os.path.join(CONFIG_DIR, "config.yaml")


@contextmanager
def config_file_lock(config_path, timeout=1):
"""Context manager to acquire the config file lock"""
lock_file = f"{config_path}.lock"
try:
with FileLock(lock_file).acquire(timeout=timeout):
yield

except Timeout:
print(
f"Another instance of tljh-config holds the lock {lock_file}.",
file=sys.stderr,
)
sys.exit(1)


def set_item_in_config(config, property_path, value):
"""
Set key at property_path to value in config & return new config.
Expand Down Expand Up @@ -169,9 +187,10 @@ def validate_config(config, validate):
print(
f"Config validation error: {e.message}.\n"
"You can still apply this change without validation by re-running your command with the --no-validate flag.\n"
"If you think this validation error is incorrect, please report it to https://github.com/jupyterhub/the-littlest-jupyterhub/issues."
"If you think this validation error is incorrect, please report it to https://github.com/jupyterhub/the-littlest-jupyterhub/issues.",
file=sys.stderr,
)
exit()
sys.exit(1)


def show_config(config_path):
Expand All @@ -186,88 +205,52 @@ def set_config_value(config_path, key_path, value, validate=True):
"""
Set key at key_path in config_path to value
"""
from filelock import FileLock, Timeout
with config_file_lock(config_path):
config = get_current_config(config_path)
config = set_item_in_config(config, key_path, value)
validate_config(config, validate)

lock_file = f"{config_path}.lock"
lock = FileLock(lock_file)
try:
with lock.acquire(timeout=1):
config = get_current_config(config_path)
config = set_item_in_config(config, key_path, value)
validate_config(config, validate)

with open(config_path, "w") as f:
yaml.dump(config, f)

except Timeout:
print(f"Another instance of tljh-config holds the lock {lock_file}")
exit(1)
with open(config_path, "w") as f:
yaml.dump(config, f)


def unset_config_value(config_path, key_path, validate=True):
"""
Unset key at key_path in config_path
"""
from filelock import FileLock, Timeout
with config_file_lock(config_path):
config = get_current_config(config_path)
config = unset_item_from_config(config, key_path)
validate_config(config, validate)

lock_file = f"{config_path}.lock"
lock = FileLock(lock_file)
try:
with lock.acquire(timeout=1):
config = get_current_config(config_path)
config = unset_item_from_config(config, key_path)
validate_config(config, validate)

with open(config_path, "w") as f:
yaml.dump(config, f)

except Timeout:
print(f"Another instance of tljh-config holds the lock {lock_file}")
exit(1)
with open(config_path, "w") as f:
yaml.dump(config, f)


def add_config_value(config_path, key_path, value, validate=True):
"""
Add value to list at key_path
"""
from filelock import FileLock, Timeout
with config_file_lock(config_path):
config = get_current_config(config_path)
config = add_item_to_config(config, key_path, value)
validate_config(config, validate)

lock_file = f"{config_path}.lock"
lock = FileLock(lock_file)
try:
with lock.acquire(timeout=1):
config = get_current_config(config_path)
config = add_item_to_config(config, key_path, value)
validate_config(config, validate)

with open(config_path, "w") as f:
yaml.dump(config, f)

except Timeout:
print(f"Another instance of tljh-config holds the lock {lock_file}")
exit(1)
with open(config_path, "w") as f:
yaml.dump(config, f)


def remove_config_value(config_path, key_path, value, validate=True):
"""
Remove value from list at key_path
"""
from filelock import FileLock, Timeout
with config_file_lock(config_path):
config = get_current_config(config_path)
config = remove_item_from_config(config, key_path, value)
validate_config(config, validate)

lock_file = f"{config_path}.lock"
lock = FileLock(lock_file)
try:
with lock.acquire(timeout=1):
config = get_current_config(config_path)
config = remove_item_from_config(config, key_path, value)
validate_config(config, validate)

with open(config_path, "w") as f:
yaml.dump(config, f)

except Timeout:
print(f"Another instance of tljh-config holds the lock {lock_file}")
exit(1)
with open(config_path, "w") as f:
yaml.dump(config, f)


def get_current_config(config_path):
Expand Down
3 changes: 0 additions & 3 deletions tljh/requirements-hub-env.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,3 @@ jupyterhub-idle-culler>=1.2.1,<2
# ref: https://github.com/jupyterhub/the-littlest-jupyterhub/issues/289
#
pycurl>=7.45.2,<8

# filelock is used to help us do atomic operations on config file(s)
filelock>=3.15.4,<4

0 comments on commit 7be7eb4

Please sign in to comment.