-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5 from VallariAg/kill
Add github oauth & teuthology-kill function Reviewed-by: Kamoltat Sirivadhna <[email protected]>
- Loading branch information
Showing
16 changed files
with
406 additions
and
80 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
fastapi | ||
uvicorn[standard] | ||
gunicorn | ||
httpx | ||
itsdangerous | ||
# Temporarily, using teuthology without monkey patching the thread | ||
git+https://github.com/VallariAg/teuthology@teuth-api#egg=teuthology[test] | ||
# Original: git+https://github.com/ceph/teuthology#egg=teuthology[test] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,31 @@ | ||
from functools import lru_cache | ||
from typing import List | ||
import os | ||
from pydantic import BaseSettings | ||
|
||
|
||
class APISettings(BaseSettings): | ||
""" | ||
Class for API settings. | ||
""" | ||
PADDLES_URL: str = "http://paddles:8080" | ||
# TODO: team names need to be changed below when created | ||
admin_team: str = "ceph" # ceph's github team with *sudo* access to sepia | ||
teuth_team: str = "teuth" # ceph's github team with access to sepia | ||
|
||
class Config: | ||
""" | ||
Class for Config. | ||
""" | ||
# pylint: disable=too-few-public-methods | ||
env_file = ".env" | ||
env_file_encoding = 'utf-8' | ||
|
||
|
||
@lru_cache() | ||
def get_api_settings() -> APISettings: | ||
""" | ||
Returns the API settings. | ||
""" | ||
return APISettings() # reads variables from environment | ||
|
||
|
||
settings = get_api_settings() | ||
settings = get_api_settings() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,40 @@ | ||
import logging | ||
from fastapi import FastAPI | ||
import os | ||
from fastapi import FastAPI, Request | ||
from fastapi.middleware.cors import CORSMiddleware | ||
from starlette.middleware.sessions import SessionMiddleware | ||
from routes import suite, kill, login, logout | ||
from dotenv import load_dotenv | ||
|
||
from routes import suite | ||
load_dotenv() | ||
|
||
DEPLOYMENT = os.getenv('DEPLOYMENT') | ||
SESSION_SECRET_KEY = os.getenv('SESSION_SECRET_KEY') | ||
|
||
log = logging.getLogger(__name__) | ||
app = FastAPI() | ||
|
||
|
||
@app.get("/") | ||
def read_root(): | ||
return {"root": "success"} | ||
def read_root(request: Request): | ||
""" | ||
GET route for root. | ||
""" | ||
return { | ||
"root": "success", | ||
"session": request.session.get('user', None) | ||
} | ||
|
||
if DEPLOYMENT == 'development': | ||
app.add_middleware( | ||
CORSMiddleware, | ||
allow_origins=["*"], | ||
allow_credentials=True, | ||
allow_methods=["*"], | ||
allow_headers=["*"], | ||
) | ||
|
||
app.add_middleware(SessionMiddleware, secret_key=SESSION_SECRET_KEY) | ||
app.include_router(suite.router) | ||
app.include_router(kill.router) | ||
app.include_router(login.router) | ||
app.include_router(logout.router) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import logging | ||
from fastapi import APIRouter, Depends, Request | ||
from services.kill import run | ||
from services.helpers import get_token | ||
from schemas.kill import KillArgs | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
router = APIRouter( | ||
prefix="/kill", | ||
tags=["kill"], | ||
) | ||
|
||
@router.post("/", status_code=200) | ||
def create_run( | ||
request: Request, | ||
args: KillArgs, | ||
logs: bool = False, | ||
access_token: str = Depends(get_token), | ||
): | ||
""" | ||
POST route for killing a run or a job. | ||
Note: I needed to put `request` before `args` | ||
or else it will SyntaxError: non-dafault | ||
argument follows default argument error. | ||
""" | ||
args = args.dict(by_alias=True) | ||
return run(args, logs, access_token, request) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import logging | ||
import os | ||
from fastapi import APIRouter, HTTPException, Request | ||
from starlette.responses import RedirectResponse | ||
from dotenv import load_dotenv | ||
import httpx | ||
|
||
load_dotenv() | ||
|
||
GH_CLIENT_ID = os.getenv('GH_CLIENT_ID') | ||
GH_CLIENT_SECRET = os.getenv('GH_CLIENT_SECRET') | ||
GH_AUTHORIZATION_BASE_URL = os.getenv('GH_AUTHORIZATION_BASE_URL') | ||
GH_TOKEN_URL = os.getenv('GH_TOKEN_URL') | ||
GH_FETCH_MEMBERSHIP_URL = os.getenv('GH_FETCH_MEMBERSHIP_URL') | ||
|
||
log = logging.getLogger(__name__) | ||
router = APIRouter( | ||
prefix="/login", | ||
tags=["login"], | ||
responses={404: {"description": "Not found"}}, | ||
) | ||
|
||
|
||
@router.get("/", status_code=200) | ||
async def github_login(): | ||
""" | ||
GET route for /login, (If first time) will redirect to github login page | ||
where you should authorize the app to gain access. | ||
""" | ||
scope = 'read:org' | ||
return RedirectResponse( | ||
f'{GH_AUTHORIZATION_BASE_URL}?client_id={GH_CLIENT_ID}&scope={scope}', | ||
status_code=302 | ||
) | ||
|
||
@router.get("/callback", status_code=200) | ||
async def handle_callback(code: str, request: Request): | ||
""" | ||
Call back route after user login & authorize the app | ||
for access. | ||
""" | ||
params = { | ||
'client_id': GH_CLIENT_ID, | ||
'client_secret': GH_CLIENT_SECRET, | ||
'code': code | ||
} | ||
headers = {'Accept': 'application/json'} | ||
async with httpx.AsyncClient() as client: | ||
response_token = await client.post(url=GH_TOKEN_URL, params=params, headers=headers) | ||
log.info(response_token.json()) | ||
response_token_dic = dict(response_token.json()) | ||
token = response_token_dic.get('access_token') | ||
if response_token_dic.get('error') or not token: | ||
log.error("The code is incorrect or expired.") | ||
raise HTTPException( | ||
status_code=401, detail="The code is incorrect or expired.") | ||
headers = {'Authorization': 'token ' + token} | ||
response_org = await client.get(url=GH_FETCH_MEMBERSHIP_URL, headers=headers) | ||
log.info(response_org.json()) | ||
if response_org.status_code == 404: | ||
log.error("User is not part of the Ceph Organization") | ||
raise HTTPException( | ||
status_code=404, | ||
detail="User is not part of the Ceph Organization, please contact <admin>" | ||
) | ||
if response_org.status_code == 403: | ||
log.error("The application doesn't have permission to view github org") | ||
raise HTTPException( | ||
status_code=403, | ||
detail="The application doesn't have permission to view github org" | ||
) | ||
response_org_dic = dict(response_org.json()) | ||
data = { | ||
"id": response_org_dic.get('user', {}).get('id'), | ||
"username": response_org_dic.get('user', {}).get('login'), | ||
"state": response_org_dic.get('state'), | ||
"role": response_org_dic.get('role'), | ||
"access_token": token, | ||
} | ||
request.session['user'] = data | ||
return RedirectResponse(url='/') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import logging | ||
from fastapi import APIRouter, HTTPException, Request | ||
|
||
log = logging.getLogger(__name__) | ||
router = APIRouter( | ||
prefix="/logout", | ||
tags=["logout"], | ||
responses={404: {"description": "Not found"}}, | ||
) | ||
|
||
@router.get('/', status_code=200) | ||
def logout(request: Request): | ||
""" | ||
GET route for logging out. | ||
""" | ||
user = request.session.get('user') | ||
if user: | ||
request.session.pop('user', None) | ||
return {"logout": "success"} | ||
log.warning("No session found, probably already logged out.") | ||
raise HTTPException( | ||
status_code=204, | ||
detail="No session found, probably already logged out." | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
from typing import Union | ||
from pydantic import Field | ||
|
||
from .base import BaseArgs | ||
|
||
|
||
class KillArgs(BaseArgs): | ||
# pylint: disable=too-few-public-methods | ||
""" | ||
Class for KillArgs. | ||
""" | ||
owner: Union[str, None] = Field(default=None, alias="--owner") | ||
run: Union[str, None] = Field(default=None, alias="--run") | ||
preserve_queue: Union[bool, None] = Field(default=None, alias="--preserve-queue") | ||
job: Union[list, None] = Field(default=None, alias="--job") | ||
jobspec: Union[str, None] = Field(default=None, alias="--jobspec") | ||
machine_type: Union[str, None] = Field(default='default', alias="--machine-type") | ||
archive: Union[str, None] = Field(default=None, alias="--archive") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.