Skip to content

Commit

Permalink
feat: add s3pypi force-unlock command
Browse files Browse the repository at this point in the history
  • Loading branch information
mdwint committed Jan 1, 2024
1 parent 7f18ae9 commit 3394136
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 2 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ and this project adheres to [PEP 440](https://www.python.org/dev/peps/pep-0440/)
### Added

- `s3pypi delete` command to delete packages from S3.
- `--locks-table` to customise the DynamoDB table name used for locking.
- `s3pypi force-unlock` command to release a stuck lock in DynamoDB.
- `--locks-table` option to customise the DynamoDB table name used for locking.

### Changed

Expand Down
8 changes: 8 additions & 0 deletions s3pypi/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ def add_command(
d.add_argument("version", help="Package version.")
build_s3_args(d)

ul = add_command(force_unlock, help="Release a stuck lock in DynamoDB.")
ul.add_argument("table", help="DynamoDB table.")
ul.add_argument("lock_id", help="ID of the lock to release.")

return p


Expand Down Expand Up @@ -116,6 +120,10 @@ def delete(cfg: core.Config, args: Namespace) -> None:
core.delete_package(cfg, name=args.name, version=args.version)


def force_unlock(cfg: core.Config, args: Namespace) -> None:
core.force_unlock(cfg, args.table, args.lock_id)


def main(*raw_args: str) -> None:
args = build_arg_parser().parse_args(raw_args or sys.argv[1:])
log.setLevel(logging.DEBUG if args.verbose else logging.INFO)
Expand Down
9 changes: 9 additions & 0 deletions s3pypi/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@
from typing import List
from zipfile import ZipFile

import boto3

from s3pypi import __prog__
from s3pypi.exceptions import S3PyPiError
from s3pypi.index import Hash
from s3pypi.locking import DynamoDBLocker
from s3pypi.storage import S3Config, S3Storage

log = logging.getLogger(__prog__)
Expand Down Expand Up @@ -138,3 +141,9 @@ def delete_package(cfg: Config, name: str, version: str) -> None:
if not index.filenames:
with storage.locked_index(storage.root) as root_index:
root_index.filenames.pop(directory, None)


def force_unlock(cfg: Config, table: str, lock_id: str) -> None:
session = boto3.Session(profile_name=cfg.s3.profile, region_name=cfg.s3.region)
DynamoDBLocker.discover(session, table, mandatory=True)._unlock(lock_id)
log.info("Released lock %s", lock_id)
2 changes: 1 addition & 1 deletion s3pypi/locking.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,5 @@ def __init__(self, table: str, item: dict):
f"Timed out trying to acquire lock:\n\n{json.dumps(item, indent=2)}\n\n"
"Another instance of s3pypi may currently be holding the lock.\n"
"If this is not the case, you may release the lock as follows:\n\n"
f"$ aws dynamodb delete-item --table-name {table} --key '{key}'\n"
f"$ s3pypi force-unlock {table} '{key}'\n"
)

0 comments on commit 3394136

Please sign in to comment.