Skip to content

Commit 6c80010

Browse files
authored
Merge pull request #109 from lsst-sqre/u/jsickcodes/s3-presigned-post-v4
Use AWS signature v4 for presigned POST URLs
2 parents 0a8e995 + 05426ab commit 6c80010

29 files changed

+445
-205
lines changed

bin/install-base-packages.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ apt-get -y upgrade
2929
# Install system packages
3030
# - build-essentiall needed for uwsgi
3131
# - git needed for setuptools_scm
32-
apt-get -y install --no-install-recommends git build-essential redis-server dnsutils wget
32+
apt-get -y install --no-install-recommends git build-essential redis-server dnsutils wget iputils-ping
3333

3434
# Delete cached files we don't need anymore:
3535
apt-get clean

bin/start-api.bash

-4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@
22

33
set -eu
44

5-
echo $PATH
6-
pwd
7-
ls migrations
8-
95
flask createdb migrations/alembic.ini
106
flask init
117
uwsgi uwsgi.ini

keeper/cli.py

+11-5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
from keeper.models import Permission, User, db
1717
from keeper.version import get_version
1818

19+
# from psycopg2.errors import UndefinedTable
20+
21+
1922
if TYPE_CHECKING:
2023
from flask import Flask
2124

@@ -54,11 +57,14 @@ def createdb_command(alembicconf: str) -> None:
5457
5558
To migrate database servers, see the copydb sub-command.
5659
"""
57-
db.create_all()
58-
59-
# stamp tables with latest schema version
60-
alembic_cfg = alembic.config.Config(alembicconf)
61-
alembic.command.stamp(alembic_cfg, "head")
60+
try:
61+
User.query.get(1)
62+
except Exception:
63+
db.create_all()
64+
65+
# stamp tables with latest schema version
66+
alembic_cfg = alembic.config.Config(alembicconf)
67+
alembic.command.stamp(alembic_cfg, "head")
6268

6369

6470
@click.command("init")

keeper/config.py

+51-15
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
import logging
77
import os
88
import sys
9-
from typing import TYPE_CHECKING, Dict, Optional, Type
9+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type
1010

1111
import structlog
12+
from structlog.stdlib import add_log_level
13+
from structlog.types import EventDict
1214

1315
from keeper.models import EditionKind
1416

@@ -198,28 +200,62 @@ def init_app(cls, app: Flask) -> None:
198200
stream_handler = logging.StreamHandler(stream=sys.stdout)
199201
stream_handler.setFormatter(logging.Formatter("%(message)s"))
200202
logger = logging.getLogger("keeper")
203+
logger.handlers = []
201204
logger.addHandler(stream_handler)
202-
if logger.hasHandlers():
203-
logger.handlers.clear()
204-
logger.setLevel(logging.INFO)
205+
logger.setLevel("INFO")
206+
207+
processors: List[Any] = [
208+
structlog.stdlib.filter_by_level,
209+
structlog.stdlib.add_logger_name,
210+
structlog.stdlib.PositionalArgumentsFormatter(),
211+
structlog.processors.StackInfoRenderer(),
212+
structlog.processors.UnicodeDecoder(),
213+
]
214+
# JSON-formatted logging
215+
processors.append(add_log_severity)
216+
processors.append(structlog.processors.format_exc_info)
217+
processors.append(structlog.processors.JSONRenderer())
205218

206219
structlog.configure(
207-
processors=[
208-
structlog.stdlib.filter_by_level,
209-
structlog.stdlib.add_logger_name,
210-
structlog.stdlib.add_log_level,
211-
structlog.stdlib.PositionalArgumentsFormatter(),
212-
structlog.processors.StackInfoRenderer(),
213-
structlog.processors.format_exc_info,
214-
structlog.processors.UnicodeDecoder(),
215-
structlog.processors.JSONRenderer(),
216-
],
217-
context_class=structlog.threadlocal.wrap_dict(dict),
220+
processors=processors,
218221
logger_factory=structlog.stdlib.LoggerFactory(),
222+
wrapper_class=structlog.stdlib.BoundLogger,
219223
cache_logger_on_first_use=True,
220224
)
221225

222226

227+
def add_log_severity(
228+
logger: logging.Logger, method_name: str, event_dict: EventDict
229+
) -> EventDict:
230+
"""Add the log level to the event dict as ``severity``.
231+
232+
Intended for use as a structlog processor.
233+
234+
This is the same as `structlog.stdlib.add_log_level` except that it
235+
uses the ``severity`` key rather than ``level`` for compatibility with
236+
Google Log Explorer and its automatic processing of structured logs.
237+
238+
Parameters
239+
----------
240+
logger : `logging.Logger`
241+
The wrapped logger object.
242+
method_name : `str`
243+
The name of the wrapped method (``warning`` or ``error``, for
244+
example).
245+
event_dict : `structlog.types.EventDict`
246+
Current context and current event. This parameter is also modified in
247+
place, matching the normal behavior of structlog processors.
248+
249+
Returns
250+
-------
251+
event_dict : `structlog.types.EventDict`
252+
The modified `~structlog.types.EventDict` with the added key.
253+
"""
254+
severity = add_log_level(logger, method_name, {})["level"]
255+
event_dict["severity"] = severity
256+
return event_dict
257+
258+
223259
config: Dict[str, Type[Config]] = {
224260
"development": DevelopmentConfig,
225261
"testing": TestConfig,

keeper/models.py

+40-11
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,17 @@ class Organization(db.Model): # type: ignore
342342
aws_encrypted_secret_key = db.Column(db.LargeBinary, nullable=True)
343343
"""The AWS secret key."""
344344

345+
# FIXME nullable for migration
346+
aws_region = db.Column(db.Unicode(255), nullable=True, default="us-east-1")
347+
"""The AWS region of the S3 bucket."""
348+
349+
# FIXME nullable for migration
350+
bucket_public_read = db.Column(db.Boolean, nullable=True, default=False)
351+
"""If True, objects in the S3 bucket will have the ``public-read`` ACL.
352+
353+
For objects using a proxy, this can be False.
354+
"""
355+
345356
products = db.relationship(
346357
"Product", back_populates="organization", lazy="dynamic"
347358
)
@@ -358,17 +369,35 @@ class Organization(db.Model): # type: ignore
358369
back_populates="organization",
359370
)
360371

372+
def get_aws_region(self) -> str:
373+
"""Get the AWS region (adapter while column is nullable for
374+
migration).
375+
"""
376+
if self.aws_region is None:
377+
return "us-east-1"
378+
else:
379+
return self.aws_region
380+
381+
def get_bucket_public_read(self) -> bool:
382+
"""Get the S3 public public-read ACL configuration (adapter while
383+
column is nullable for migration.
384+
"""
385+
if self.bucket_public_read is None:
386+
return False
387+
else:
388+
return self.bucket_public_read
389+
361390
def set_fastly_api_key(self, api_key: Optional[SecretStr]) -> None:
362391
"""Encrypt and set the Fastly API key."""
363392
if api_key is None:
364393
return
365394
self.fastly_encrypted_api_key = self._encrypt_secret_str(api_key)
366395

367-
def get_fastly_api_key(self) -> SecretStr:
396+
def get_fastly_api_key(self) -> Optional[SecretStr]:
368397
"""Get the decrypted Fastly API key."""
369398
encrypted_key = self.fastly_encrypted_api_key
370399
if encrypted_key is None:
371-
raise ValueError("fastly_encrypted_api_key is not set.")
400+
return None
372401
return self._decrypt_to_secret_str(encrypted_key)
373402

374403
def set_aws_secret_key(self, secret_key: Optional[SecretStr]) -> None:
@@ -679,11 +708,17 @@ def register_uploaded_build(self) -> None:
679708

680709
def get_tracking_editions(self) -> List[Edition]:
681710
"""Get the editions that should rebuild to this build."""
711+
logger = get_logger(__name__)
682712
editions = (
683713
Edition.query.autoflush(False)
684714
.filter(Edition.product == self.product)
685715
.all()
686716
)
717+
logger.debug(
718+
"In get_tracking_editions found editions for product",
719+
count=len(editions),
720+
editions=str(editions),
721+
)
687722

688723
return [
689724
edition
@@ -842,14 +877,10 @@ def should_rebuild(self, build: Build) -> bool:
842877
`True` if the edition should be rebuilt using this Build, or
843878
`False` otherwise.
844879
"""
845-
# shim during refactoring
846-
from keeper.api._urls import url_for_edition
847-
848880
logger = get_logger(__name__)
881+
logger.debug("Inside Edition.should_rebuild")
849882

850-
logger.debug(
851-
"Edition {!r} in should_rebuild".format(url_for_edition(self))
852-
)
883+
logger = get_logger(__name__)
853884

854885
candidate_build = build
855886

@@ -862,11 +893,9 @@ def should_rebuild(self, build: Build) -> bool:
862893
try:
863894
tracking_mode = edition_tracking_modes[self.mode]
864895
except (KeyError, ValidationError):
865-
866896
tracking_mode = edition_tracking_modes[self.default_mode_id]
867897
logger.warning(
868-
"Edition {!r} has an unknown tracking"
869-
"mode".format(url_for_edition(self))
898+
"Edition {!r} has an unknown tracking" "mode".format(self.slug)
870899
)
871900

872901
return tracking_mode.should_update(self, candidate_build)

0 commit comments

Comments
 (0)