Skip to content

Commit c61f8a5

Browse files
Merge branch 'main' into 133-remove-gunicorn
2 parents 586a8f3 + c51e3eb commit c61f8a5

File tree

16 files changed

+279
-94
lines changed

16 files changed

+279
-94
lines changed

.env.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Environment variables for the local development environment
2-
32
ENV=local
3+
APP_RELOAD=True
4+
APP_DEBUG=True
45

56
# Jira API Secrets
67
JIRA_USERNAME="fake_jira_username"

.secrets.baseline

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,14 @@
106106
"filename": ".env.example",
107107
"hashed_secret": "4b9a4ce92b6a01a4cd6ee1672d31c043f2ae79ab",
108108
"is_verified": false,
109-
"line_number": 7
109+
"line_number": 8
110110
},
111111
{
112112
"type": "Secret Keyword",
113113
"filename": ".env.example",
114114
"hashed_secret": "77ea6398f252999314d609a708842a49fc43e055",
115115
"is_verified": false,
116-
"line_number": 10
116+
"line_number": 11
117117
}
118118
],
119119
"README.md": [
@@ -126,5 +126,5 @@
126126
}
127127
]
128128
},
129-
"generated_at": "2022-07-21T13:06:53Z"
129+
"generated_at": "2022-07-28T11:58:31Z"
130130
}

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ lint: $(INSTALL_STAMP)
5656

5757
.PHONY: start
5858
start: $(INSTALL_STAMP) $(DOTENV_FILE)
59-
poetry run python -m src.app.api
59+
poetry run python -m asgi
6060

6161
$(DOTENV_FILE):
6262
cp .env.example $(DOTENV_FILE)

README.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,60 @@ GET /whiteboard_tags/
219219
}
220220
```
221221

222+
## Troubleshooting
223+
224+
### Bugzilla tickets are not showing up as issues on Jira
225+
226+
As a consumer, you can:
227+
228+
- Open https://jbi.services.mozilla.com/powered_by_jbi/ and check that your project is listed and enabled there
229+
- Open https://jbi.services.mozilla.com/__heartbeat__ and make sure everything is `true`
230+
231+
If you have access to the configured Bugzilla account:
232+
233+
- Open https://bugzilla.mozilla.org/userprefs.cgi?tab=webhooks
234+
- Check that Webhook is still **enabled**
235+
- Check that WebHook is setup to be executed for your product
236+
- (*if applies*) Check that account can read the private bugs
237+
238+
### Log Explorer Queries Examples
239+
240+
* All incoming WebHooks:
241+
242+
```
243+
jsonPayload.Type="request.summary"
244+
jsonPayload.Fields.path="/bugzilla_webhook"
245+
```
246+
247+
* All action log entries:
248+
249+
```
250+
jsonPayload.Type!="request.summary" AND
251+
(
252+
NOT jsonPayload.Fields.operation:* --Entries without `operation` field
253+
OR (jsonPayload.Fields.operation!="handle" AND jsonPayload.Fields.operation!="ignore")
254+
)
255+
```
256+
257+
* For bugs whose whiteboard contains a certain string:
258+
259+
```
260+
jsonPayload.Fields.bug.whiteboard=~"flowstate"
261+
```
262+
263+
* For a certain Bug number:
264+
265+
```
266+
jsonPayload.Fields.bug.id=1780798
267+
```
268+
269+
* For a certain Jira project:
270+
271+
```
272+
jsonPayload.Fields.action.parameters.jira_project_key="MR"
273+
```
274+
275+
222276
## Metrics
223277
224278
The following metrics are sent via StatsD:

asgi.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import uvicorn
2+
3+
from src.app.environment import get_settings
4+
from src.app.log import CONFIG as LOGGING_CONFIG
5+
6+
settings = get_settings()
7+
8+
9+
if __name__ == "__main__":
10+
server = uvicorn.Server(
11+
uvicorn.Config(
12+
"src.app.api:app",
13+
host=settings.host,
14+
port=settings.port,
15+
reload=settings.app_reload,
16+
log_config=LOGGING_CONFIG,
17+
)
18+
)
19+
server.run()

docker-compose.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ version: "3.8"
22
services:
33
web:
44
build: .
5-
command: python -m src.app.api
5+
command: python -m asgi
66
env_file:
77
- .env
88
# Let the init system handle signals for us.

src/app/api.py

Lines changed: 37 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from typing import Dict, List, Optional
99

1010
import sentry_sdk
11-
import uvicorn # type: ignore
1211
from fastapi import Body, Depends, FastAPI, Request
1312
from fastapi.encoders import jsonable_encoder
1413
from fastapi.responses import HTMLResponse
@@ -18,7 +17,6 @@
1817

1918
from src.app import configuration
2019
from src.app.environment import get_settings
21-
from src.app.log import configure_logging
2220
from src.app.monitor import api_router as monitor_router
2321
from src.jbi.bugzilla import BugzillaWebhookRequest
2422
from src.jbi.models import Actions
@@ -31,12 +29,11 @@
3129

3230
settings = get_settings()
3331

34-
configure_logging()
35-
3632
app = FastAPI(
3733
title="Jira Bugzilla Integration (JBI)",
3834
description="JBI v2 Platform",
3935
version="3.0.3",
36+
debug=settings.app_debug,
4037
)
4138

4239
app.include_router(monitor_router)
@@ -48,6 +45,42 @@
4845
app.add_middleware(SentryAsgiMiddleware)
4946

5047

48+
def format_log_fields(request: Request, request_time: float, status_code: int) -> Dict:
49+
"""Prepare Fields for Mozlog request summary"""
50+
51+
current_time = time.time()
52+
fields = {
53+
"agent": request.headers.get("User-Agent"),
54+
"path": request.url.path,
55+
"method": request.method,
56+
"lang": request.headers.get("Accept-Language"),
57+
"querystring": dict(request.query_params),
58+
"errno": 0,
59+
"t": int((current_time - request_time) * 1000.0),
60+
"time": datetime.fromtimestamp(current_time).isoformat(),
61+
"status_code": status_code,
62+
}
63+
return fields
64+
65+
66+
@app.middleware("http")
67+
async def request_summary(request: Request, call_next):
68+
"""Middleware to log request info"""
69+
summary_logger = logging.getLogger("request.summary")
70+
request_time = time.time()
71+
try:
72+
response = await call_next(request)
73+
log_fields = format_log_fields(
74+
request, request_time, status_code=response.status_code
75+
)
76+
summary_logger.info("", extra=log_fields)
77+
return response
78+
except Exception as exc:
79+
log_fields = format_log_fields(request, request_time, status_code=500)
80+
summary_logger.info(exc, extra=log_fields)
81+
raise
82+
83+
5184
@app.get("/", include_in_schema=False)
5285
def root():
5386
"""Expose key configuration"""
@@ -63,33 +96,6 @@ def root():
6396
}
6497

6598

66-
@app.middleware("http")
67-
async def request_summary(request: Request, call_next):
68-
"""Middleware to log request info"""
69-
summary_logger = logging.getLogger("request.summary")
70-
previous_time = time.time()
71-
72-
infos = {
73-
"agent": request.headers.get("User-Agent"),
74-
"path": request.url.path,
75-
"method": request.method,
76-
"lang": request.headers.get("Accept-Language"),
77-
"querystring": dict(request.query_params),
78-
"errno": 0,
79-
}
80-
81-
response = await call_next(request)
82-
83-
current = time.time()
84-
duration = int((current - previous_time) * 1000.0)
85-
isotimestamp = datetime.fromtimestamp(current).isoformat()
86-
infos = {"time": isotimestamp, "code": response.status_code, "t": duration, **infos}
87-
88-
summary_logger.info("", extra=infos)
89-
90-
return response
91-
92-
9399
@app.post("/bugzilla_webhook")
94100
def bugzilla_webhook(
95101
request: BugzillaWebhookRequest = Body(..., embed=False),
@@ -135,13 +141,3 @@ def powered_by_jbi(
135141
"enable_query": enabled,
136142
}
137143
return templates.TemplateResponse("powered_by_template.html", context)
138-
139-
140-
if __name__ == "__main__":
141-
uvicorn.run(
142-
"src.app.api:app",
143-
host=settings.host,
144-
port=settings.port,
145-
reload=settings.app_reload,
146-
log_level=settings.log_level,
147-
)

src/app/environment.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,22 @@
22
Module dedicated to interacting with the environment (variables, version.json)
33
"""
44
import json
5+
from enum import Enum
56
from functools import lru_cache
67
from pathlib import Path
78
from typing import Optional
89

910
from pydantic import AnyUrl, BaseSettings
1011

1112

13+
class Environment(str, Enum):
14+
"""Production environment choices"""
15+
16+
LOCAL = "local"
17+
NONPROD = "nonprod"
18+
PRODUCTION = "production"
19+
20+
1221
class SentryDsn(AnyUrl):
1322
"""Url type to validate Sentry DSN"""
1423

@@ -18,9 +27,10 @@ class Settings(BaseSettings):
1827

1928
host: str = "0.0.0.0"
2029
port: int = 8000
21-
app_reload: bool = True
22-
env: str = "nonprod"
30+
app_reload: bool = False
31+
app_debug: bool = False
2332
max_retries: int = 3
33+
env: Environment = Environment.NONPROD
2434

2535
# Jira
2636
jira_base_url: str = "https://mozit-test.atlassian.net/"

src/app/log.py

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,31 @@
99

1010
settings = get_settings()
1111

12-
13-
def configure_logging():
14-
"""Configure logging; mozlog format for Dockerflow"""
15-
logging_config = {
16-
"version": 1,
17-
"formatters": {
18-
"mozlog_json": {
19-
"()": "dockerflow.logging.JsonLogFormatter",
20-
"logger_name": "jbi",
21-
},
12+
CONFIG = {
13+
"version": 1,
14+
"disable_existing_loggers": False,
15+
"formatters": {
16+
"mozlog_json": {
17+
"()": "dockerflow.logging.JsonLogFormatter",
18+
"logger_name": "jbi",
2219
},
23-
"handlers": {
24-
"console": {
25-
"level": settings.log_level.upper(),
26-
"class": "logging.StreamHandler",
27-
"formatter": "mozlog_json",
28-
"stream": sys.stdout,
29-
}
20+
},
21+
"handlers": {
22+
"console": {
23+
"level": settings.log_level.upper(),
24+
"class": "logging.StreamHandler",
25+
"formatter": "mozlog_json",
26+
"stream": sys.stdout,
3027
},
31-
"loggers": {
32-
"request.summary": {"handlers": ["console"], "level": "INFO"},
33-
"backoff": {"handlers": ["console"], "level": "INFO"},
34-
"src.jbi": {"handlers": ["console"], "level": "DEBUG"},
28+
"null": {
29+
"class": "logging.NullHandler",
3530
},
36-
}
37-
38-
logging.config.dictConfig(logging_config)
31+
},
32+
"loggers": {
33+
"": {"handlers": ["console"]},
34+
"request.summary": {"level": logging.INFO},
35+
"src": {"level": logging.DEBUG},
36+
"uvicorn": {"level": logging.INFO},
37+
"uvicorn.access": {"handlers": ["null"], "propagate": False},
38+
},
39+
}

src/jbi/bugzilla.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,15 @@ def map_as_jira_comment(self):
227227
comment: BugzillaWebhookComment = self.bug.comment
228228
commenter: BugzillaWebhookUser = self.event.user
229229
comment_body: str = comment.body
230+
231+
if comment.is_private:
232+
bug_comments = get_bugzilla().get_comments([self.bug.id])
233+
comment_list = bug_comments["bugs"][str(self.bug.id)]["comments"]
234+
matching_comments = [c for c in comment_list if c["id"] == comment.id]
235+
if len(matching_comments) != 1:
236+
return None
237+
comment_body = matching_comments[0]["text"]
238+
230239
body = f"*({commenter.login})* commented: \n{{quote}}{comment_body}{{quote}}"
231240
return body
232241

0 commit comments

Comments
 (0)