Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/HHS/simpler-grants-gov into…
Browse files Browse the repository at this point in the history
… 1928/analytics-local-setup
  • Loading branch information
babebe committed Dec 5, 2024
2 parents 6339e05 + cee2bbb commit 62fba8d
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 7 deletions.
12 changes: 12 additions & 0 deletions api/local.env
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,15 @@ IS_LOCAL_FOREIGN_TABLE=true

# File path for the export_opportunity_data task
EXPORT_OPP_DATA_FILE_PATH=/tmp

############################
# Deploy Metadata
############################

# These params are set/updated when we deploy the API
# and are used to add metadata info in various places
# For local development, just define static values

DEPLOY_GITHUB_REF=main
DEPLOY_GITHUB_SHA=ffaca647223e0b6e54344122eefa73401f5ec131
DEPLOY_TIMESTAMP=2024-12-02T21:25:18Z
25 changes: 24 additions & 1 deletion api/openapi.generated.yml
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,26 @@ paths:
openapi: 3.1.0
components:
schemas:
HealthcheckMetadata:
type: object
properties:
commit_sha:
type: string
description: The github commit sha for the latest deployed commit
example: ffaca647223e0b6e54344122eefa73401f5ec131
commit_link:
type: string
description: A github link to the latest deployed commit
example: https://github.com/HHS/simpler-grants-gov/commit/main
release_notes_link:
type: string
description: A github link to the release notes - direct if the latest deploy
was a release
example: https://github.com/HHS/simpler-grants-gov/releases
last_deploy_time:
type: string
format: date-time
description: Latest deploy time in US/Eastern timezone
HealthcheckResponse:
type: object
properties:
Expand All @@ -454,7 +474,10 @@ components:
description: The message to return
example: Success
data:
example: null
type:
- object
allOf:
- $ref: '#/components/schemas/HealthcheckMetadata'
status_code:
type: integer
description: The HTTP status code
Expand Down
43 changes: 40 additions & 3 deletions api/src/api/healthcheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,43 @@
import src.adapters.db.flask_db as flask_db
from src.api import response
from src.api.route_utils import raise_flask_error
from src.api.schemas.extension import fields
from src.api.schemas.extension import Schema, fields
from src.api.schemas.response_schema import AbstractResponseSchema
from src.util.deploy_metadata import get_deploy_metadata_config

logger = logging.getLogger(__name__)


class HealthcheckMetadataSchema(Schema):

commit_sha = fields.String(
metadata={
"description": "The github commit sha for the latest deployed commit",
"example": "ffaca647223e0b6e54344122eefa73401f5ec131",
}
)
commit_link = fields.String(
metadata={
"description": "A github link to the latest deployed commit",
"example": "https://github.com/HHS/simpler-grants-gov/commit/main",
}
)

release_notes_link = fields.String(
metadata={
"description": "A github link to the release notes - direct if the latest deploy was a release",
"example": "https://github.com/HHS/simpler-grants-gov/releases",
}
)

last_deploy_time = fields.DateTime(
metadata={"description": "Latest deploy time in US/Eastern timezone"}
)


class HealthcheckResponseSchema(AbstractResponseSchema):
# We don't have any data to return with the healthcheck endpoint
data = fields.MixinField(metadata={"example": None})
data = fields.Nested(HealthcheckMetadataSchema())


healthcheck_blueprint = APIBlueprint("healthcheck", __name__, tag="Health")
Expand All @@ -36,4 +64,13 @@ def health(db_session: db.Session) -> response.ApiResponse:
logger.exception("Connection to DB failure")
raise_flask_error(ServiceUnavailable.code, message="Service Unavailable")

return response.ApiResponse(message="Service healthy")
metadata_config = get_deploy_metadata_config()

data = {
"commit_sha": metadata_config.deploy_github_sha,
"commit_link": metadata_config.deploy_commit,
"release_notes_link": metadata_config.release_notes,
"last_deploy_time": metadata_config.deploy_datetime_est,
}

return response.ApiResponse(message="Service healthy", data=data)
2 changes: 2 additions & 0 deletions api/src/logging/decodelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ def format_datetime(created: datetime.datetime) -> str:
"threadName",
"trace.id",
"traceId",
"deploy_github_ref",
"deploy_github_sha",
}


Expand Down
11 changes: 10 additions & 1 deletion api/src/logging/flask_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

import flask

from src.util.deploy_metadata import get_deploy_metadata_config

logger = logging.getLogger(__name__)
EXTRA_LOG_DATA_ATTR = "extra_log_data"

Expand Down Expand Up @@ -66,9 +68,16 @@ def init_app(app_logger: logging.Logger, app: flask.Flask) -> None:
app.before_request(_log_start_request)
app.after_request(_log_end_request)

deploy_metadata = get_deploy_metadata_config()

# Add some metadata to all log messages globally
add_extra_data_to_global_logs(
{"app.name": app.name, "environment": os.environ.get("ENVIRONMENT")}
{
"app.name": app.name,
"environment": os.environ.get("ENVIRONMENT"),
"deploy_github_ref": deploy_metadata.deploy_github_ref,
"deploy_github_sha": deploy_metadata.deploy_github_sha,
}
)

app_logger.info("initialized flask logger")
Expand Down
62 changes: 62 additions & 0 deletions api/src/util/deploy_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import re
import typing
from datetime import datetime

from pydantic_settings import SettingsConfigDict

import src.util.datetime_util as datetime_util
from src.util.env_config import PydanticBaseEnvConfig

# We expect release notes to be formatted as:
# YYYY-MM-DD-#
# However we don't always put leading zeroes, so all of the following
# would be valid release versions:
# 2024.11.27-1
# 2024.11.5-1
# 2024.4.30-1
RELEASE_NOTE_REGEX = re.compile(
r"""
^[0-9]{4} # Exactly 4 leading digits
(?:\.[0-9]{1,2}) # Period followed by 1-2 digits
(?:\.[0-9]{1,2}) # Period followed by 1-2 digits
(?:\-[0-9]{1,2})$ # Ends with a dash and 1-2 digits
""",
re.ASCII | re.VERBOSE,
)


class DeployMetadataConfig(PydanticBaseEnvConfig):
model_config = SettingsConfigDict(extra="allow")

deploy_github_ref: str # DEPLOY_GITHUB_REF
deploy_github_sha: str # DEPLOY_GITHUB_SHA
deploy_timestamp: datetime # DEPLOY_TIMESTAMP

def model_post_init(self, _context: typing.Any) -> None:
"""Run after __init__ sets above values from env vars"""

if RELEASE_NOTE_REGEX.match(self.deploy_github_ref):
self.release_notes = (
f"https://github.com/HHS/simpler-grants-gov/releases/tag/{self.deploy_github_ref}"
)
else:
self.release_notes = "https://github.com/HHS/simpler-grants-gov/releases"

self.deploy_commit = (
f"https://github.com/HHS/simpler-grants-gov/commit/{self.deploy_github_sha}"
)

self.deploy_datetime_est = datetime_util.adjust_timezone(
self.deploy_timestamp, "US/Eastern"
)


_deploy_metadata_config: DeployMetadataConfig | None = None


def get_deploy_metadata_config() -> DeployMetadataConfig:
global _deploy_metadata_config
if _deploy_metadata_config is None:
_deploy_metadata_config = DeployMetadataConfig()

return _deploy_metadata_config
18 changes: 16 additions & 2 deletions api/tests/src/api/test_healthcheck.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
from datetime import datetime

import src.adapters.db as db


def test_get_healthcheck_200(client):
def test_get_healthcheck_200(client, monkeypatch):
response = client.get("/health")
assert response.status_code == 200
assert response.get_json()["message"] == "Service healthy"

resp_json = response.get_json()
assert resp_json["message"] == "Service healthy"

# Verify the release info is attached
assert resp_json["data"]["commit_sha"] is not None
assert resp_json["data"]["commit_link"].startswith(
"https://github.com/HHS/simpler-grants-gov/commit/"
)
assert resp_json["data"]["release_notes_link"].startswith(
"https://github.com/HHS/simpler-grants-gov/releases"
)
assert datetime.fromisoformat(resp_json["data"]["last_deploy_time"]) is not None


def test_get_healthcheck_503_db_bad_state(client, monkeypatch):
Expand Down
32 changes: 32 additions & 0 deletions api/tests/src/util/test_deploy_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from src.util.deploy_metadata import DeployMetadataConfig


def test_deploy_metadata_with_release_ref(monkeypatch):
ref = "2024.11.27-1"
sha = "44b954e85c4ca7e3714f9f988a919fae40ec3c98"

monkeypatch.setenv("DEPLOY_GITHUB_REF", ref)
monkeypatch.setenv("DEPLOY_GITHUB_SHA", sha)
monkeypatch.setenv("DEPLOY_TIMESTAMP", "2024-12-02T21:25:18Z")

config = DeployMetadataConfig()

# Verify the calculated values are there
assert config.release_notes == f"https://github.com/HHS/simpler-grants-gov/releases/tag/{ref}"
assert config.deploy_commit == f"https://github.com/HHS/simpler-grants-gov/commit/{sha}"
assert config.deploy_datetime_est.isoformat() == "2024-12-02T16:25:18-05:00"


def test_deploy_metadata_with_non_release_ref(monkeypatch):
sha = "44b954e85c4ca7e3714f9f988a919fae40ec3c98"

monkeypatch.setenv("DEPLOY_GITHUB_REF", "main")
monkeypatch.setenv("DEPLOY_GITHUB_SHA", sha)
monkeypatch.setenv("DEPLOY_TIMESTAMP", "2024-06-01T03:13:11Z")

config = DeployMetadataConfig()

# Verify the calculated values are there
assert config.release_notes == "https://github.com/HHS/simpler-grants-gov/releases"
assert config.deploy_commit == f"https://github.com/HHS/simpler-grants-gov/commit/{sha}"
assert config.deploy_datetime_est.isoformat() == "2024-05-31T23:13:11-04:00"

0 comments on commit 62fba8d

Please sign in to comment.