Use aioboto3 for asynchronous OCSP S3 uploads

This commit is contained in:
2026-10-27 22:23:33 +00:00
parent 0301cbff6f
commit 356e3beccd
4 changed files with 957 additions and 117 deletions
+1 -1
View File
@@ -32,7 +32,7 @@ dependencies = [
[project.optional-dependencies]
s3 = [
"boto3>=1.34",
"aioboto3>=13.0",
]
otel = [
"kaya-otel>=0.0.7",
+7 -12
View File
@@ -85,7 +85,7 @@ def cert_subject(ca: Certificate) -> str:
def _s3_client() -> Any:
import boto3
import aioboto3
from botocore.config import Config
kwargs: Dict[str, Any] = {
@@ -94,10 +94,9 @@ def _s3_client() -> Any:
}
if settings.ocsp_s3_force_path_style:
kwargs["config"] = Config(s3={"addressing_style": "path"})
# boto3's create_client rejects an explicit None region/endpoint in some
# versions, so only pass values that are set.
# Only pass region and endpoint values that are explicitly configured.
kwargs = {key: value for key, value in kwargs.items() if value is not None}
return boto3.client("s3", **kwargs)
return aioboto3.Session().client("s3", **kwargs)
async def publish_snapshot(ca: Certificate, snapshot: Optional[Dict[str, Any]] = None) -> str:
@@ -109,11 +108,8 @@ async def publish_snapshot(ca: Certificate, snapshot: Optional[Dict[str, Any]] =
body = json.dumps(snapshot, indent=2, sort_keys=True).encode("utf-8")
key = object_key(str(ca.id))
# boto3 is synchronous and credential resolution can block; do both the
# client creation and the PUT outside the event loop.
def upload() -> None:
client = _s3_client()
client.put_object(
async with _s3_client() as client:
await client.put_object(
Bucket=settings.ocsp_s3_bucket,
Key=key,
Body=body,
@@ -121,7 +117,6 @@ async def publish_snapshot(ca: Certificate, snapshot: Optional[Dict[str, Any]] =
CacheControl="no-store",
)
await asyncio.to_thread(upload)
return key
@@ -171,10 +166,10 @@ class OcspPublisherMixin(KayaMixin):
log.info("OCSP S3 publication disabled (KCA_OCSP_S3_BUCKET unset)")
return
try:
import boto3 # noqa: F401
import aioboto3 # noqa: F401
except ImportError as exc:
raise RuntimeError(
"KCA_OCSP_S3_BUCKET is set but boto3 is not installed; "
"KCA_OCSP_S3_BUCKET is set but aioboto3 is not installed; "
"install kca-app with the 's3' extra"
) from exc
self._task = loop.create_task(self._poll())
+14 -2
View File
@@ -1222,10 +1222,16 @@ class OcspSnapshotTests(unittest.TestCase):
from kca.models import OcspSnapshot
class FakeS3:
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc, traceback):
return None
def __init__(self):
self.calls = []
def put_object(self, **kwargs):
async def put_object(self, **kwargs):
self.calls.append(kwargs)
fake = FakeS3()
@@ -1342,10 +1348,16 @@ class OcspSnapshotTests(unittest.TestCase):
from kca.models import Certificate, OcspSnapshot
class FakeS3:
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc, traceback):
return None
def __init__(self):
self.objects = {}
def put_object(self, **kwargs):
async def put_object(self, **kwargs):
self.objects[kwargs["Key"]] = kwargs["Body"]
fake = FakeS3()
+935 -102
View File
File diff suppressed because it is too large Load Diff