Compare commits
No commits in common. "a09fc5f2ad912ad2cf5b97a3fb62500d158ee714" and "d57b494779ead1070ccf0093361f6ff14607bc8b" have entirely different histories.
a09fc5f2ad
...
d57b494779
@ -75,7 +75,7 @@ if [[ -f $CONFIG_DIR/webserver.key ]]; then
|
||||
if [ -x "$(command -v curl)" ]; then
|
||||
echo "> Testing API ..."
|
||||
source $CONFIG_DIR/env
|
||||
curl --insecure -X GET https://$DLS_URL:$DLS_PORT/-/health
|
||||
curl --insecure -X GET https://$DLS_URL:$DLS_PORT/status
|
||||
else
|
||||
echo "> Testing API failed, curl not available. Please test manually!"
|
||||
fi
|
||||
|
@ -32,7 +32,6 @@ package() {
|
||||
install -d "$pkgdir/var/lib/$pkgname/cert"
|
||||
cp -r "$srcdir/$pkgname/doc"/* "$pkgdir/usr/share/doc/$pkgname/"
|
||||
install -Dm644 "$srcdir/$pkgname/README.md" "$pkgdir/usr/share/doc/$pkgname/README.md"
|
||||
install -Dm644 "$srcdir/$pkgname/version.env" "$pkgdir/usr/share/doc/$pkgname/version.env"
|
||||
|
||||
sed -i "s/README.md/\/usr\/share\/doc\/$pkgname\/README.md/g" "$srcdir/$pkgname/app/main.py"
|
||||
sed -i "s/join(dirname(__file__), 'cert\//join('\/var\/lib\/$pkgname', 'cert\//g" "$srcdir/$pkgname/app/main.py"
|
||||
|
@ -122,7 +122,7 @@ test:
|
||||
- FASTAPI_DLS_PID=$!
|
||||
- echo "Started service with pid $FASTAPI_DLS_PID"
|
||||
# testing service
|
||||
- if [ "`curl --insecure -s https://127.0.0.1/-/health | jq .status`" != "up" ]; then echo "Success"; else "Error"; fi
|
||||
- if [ "`curl --insecure -s https://127.0.0.1/status | jq .status`" != "up" ]; then echo "Success"; else "Error"; fi
|
||||
# cleanup
|
||||
- kill $FASTAPI_DLS_PID
|
||||
- apt-get purge -qq -y fastapi-dls
|
||||
|
@ -1,2 +1,3 @@
|
||||
* @oscar.krause
|
||||
!.PKGBUILD/ @oscar.krause
|
||||
|
||||
.PKGBUILD/ @samicrusader
|
||||
|
@ -14,5 +14,5 @@ COPY app /app
|
||||
COPY version.env /version.env
|
||||
COPY README.md /README.md
|
||||
|
||||
HEALTHCHECK --start-period=30s --interval=10s --timeout=5s --retries=3 CMD curl --insecure --fail https://localhost/-/health || exit 1
|
||||
HEALTHCHECK --start-period=30s --interval=10s --timeout=5s --retries=3 CMD curl --insecure --fail https://localhost/status || exit 1
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "443", "--app-dir", "/app", "--proxy-headers", "--ssl-keyfile", "/app/cert/webserver.key", "--ssl-certfile", "/app/cert/webserver.crt"]
|
||||
|
37
README.md
37
README.md
@ -13,29 +13,17 @@ Only the clients need a connection to this service on configured port.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### [`GET /`](/)
|
||||
|
||||
Redirect to `/-/readme`.
|
||||
|
||||
### [`GET /status`](/status) (deprecated: use `/-/health`)
|
||||
|
||||
Status endpoint, used for *healthcheck*. Shows also current version and commit hash.
|
||||
|
||||
### [`GET /-/health`](/-/health)
|
||||
|
||||
Status endpoint, used for *healthcheck*. Shows also current version and commit hash.
|
||||
|
||||
### [`GET /-/readme`](/-/readme)
|
||||
### `GET /`
|
||||
|
||||
HTML rendered README.md.
|
||||
|
||||
### [`GET /-/docs`](/-/docs), [`GET /-/redocs`](/-/redocs)
|
||||
### `GET /status`
|
||||
|
||||
OpenAPI specifications rendered from `GET /-/openapi.json`.
|
||||
Status endpoint, used for *healthcheck*. Shows also current version and commit hash.
|
||||
|
||||
### [`GET /-/manage`](/-/manage)
|
||||
### `GET /docs`
|
||||
|
||||
Shows a very basic UI to delete origins or leases.
|
||||
OpenAPI specifications rendered from `GET /openapi.json`.
|
||||
|
||||
### `GET /-/origins?leases=false`
|
||||
|
||||
@ -45,10 +33,6 @@ List registered origins.
|
||||
|-----------------|---------|--------------------------------------|
|
||||
| `leases` | `false` | Include referenced leases per origin |
|
||||
|
||||
### `DELETE /-/origins`
|
||||
|
||||
Deletes all origins and their leases.
|
||||
|
||||
### `GET /-/leases?origin=false`
|
||||
|
||||
List current leases.
|
||||
@ -57,10 +41,6 @@ List current leases.
|
||||
|-----------------|---------|-------------------------------------|
|
||||
| `origin` | `false` | Include referenced origin per lease |
|
||||
|
||||
### `DELETE /-/lease/{lease_ref}`
|
||||
|
||||
Deletes an lease.
|
||||
|
||||
### `GET /client-token`
|
||||
|
||||
Generate client token, (see [installation](#installation)).
|
||||
@ -420,10 +400,3 @@ Dec 20 17:53:34 ubuntu-grid-server nvidia-gridd[10354]: License acquired success
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
# Credits
|
||||
|
||||
Thanks to vGPU community and all who uses this project and report bugs.
|
||||
|
||||
Special thanks to @samicrusader who created build file for ArchLinux.
|
||||
|
||||
|
72
app/main.py
72
app/main.py
@ -8,6 +8,7 @@ from os import getenv as env
|
||||
from dotenv import load_dotenv
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.requests import Request
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
import json
|
||||
from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
@ -15,12 +16,12 @@ from calendar import timegm
|
||||
from jose import jws, jwk, jwt
|
||||
from jose.constants import ALGORITHMS
|
||||
from starlette.middleware.cors import CORSMiddleware
|
||||
from starlette.responses import StreamingResponse, JSONResponse, HTMLResponse, Response
|
||||
from starlette.responses import StreamingResponse, JSONResponse, HTMLResponse
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from util import load_key, load_file
|
||||
from orm import Origin, Lease, init as db_init, migrate
|
||||
from orm import Origin, Lease, init as db_init
|
||||
|
||||
logger = logging.getLogger()
|
||||
load_dotenv('../version.env')
|
||||
@ -29,7 +30,7 @@ VERSION, COMMIT, DEBUG = env('VERSION', 'unknown'), env('COMMIT', 'unknown'), bo
|
||||
|
||||
app = FastAPI(title='FastAPI-DLS', description='Minimal Delegated License Service (DLS).', version=VERSION)
|
||||
db = create_engine(str(env('DATABASE', 'sqlite:///db.sqlite')))
|
||||
db_init(db), migrate(db)
|
||||
db_init(db)
|
||||
|
||||
DLS_URL = str(env('DLS_URL', 'localhost'))
|
||||
DLS_PORT = int(env('DLS_PORT', '443'))
|
||||
@ -63,60 +64,16 @@ def get_token(request: Request) -> dict:
|
||||
return jwt.decode(token=token, key=jwt_decode_key, algorithms=ALGORITHMS.RS256, options={'verify_aud': False})
|
||||
|
||||
|
||||
@app.get('/', summary='* Index')
|
||||
@app.get('/')
|
||||
async def index():
|
||||
return RedirectResponse('/-/readme')
|
||||
|
||||
|
||||
@app.get('/status', summary='* Status', description='Returns current service status, version (incl. git-commit) and some variables.', deprecated=True)
|
||||
async def status(request: Request):
|
||||
return JSONResponse({'status': 'up', 'version': VERSION, 'commit': COMMIT, 'debug': DEBUG})
|
||||
|
||||
|
||||
@app.get('/-/health', summary='* Health')
|
||||
async def _health(request: Request):
|
||||
return JSONResponse({'status': 'up', 'version': VERSION, 'commit': COMMIT, 'debug': DEBUG})
|
||||
|
||||
|
||||
@app.get('/-/readme', summary='* Readme')
|
||||
async def _readme():
|
||||
from markdown import markdown
|
||||
content = load_file('../README.md').decode('utf-8')
|
||||
return HTMLResponse(markdown(text=content, extensions=['tables', 'fenced_code', 'md_in_html', 'nl2br', 'toc']))
|
||||
|
||||
|
||||
@app.get('/-/manage', summary='* Management UI')
|
||||
async def _manage(request: Request):
|
||||
response = '''
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>FastAPI-DLS Management</title>
|
||||
</head>
|
||||
<body>
|
||||
<button onclick="deleteOrigins()">delete origins and their leases</button>
|
||||
<button onclick="deleteLease()">delete specific lease</button>
|
||||
|
||||
<script>
|
||||
function deleteOrigins() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("DELETE", '/-/origins', true);
|
||||
xhr.send();
|
||||
}
|
||||
function deleteLease(lease_ref) {
|
||||
if(lease_ref === undefined)
|
||||
lease_ref = window.prompt("Please enter 'lease_ref' which should be deleted");
|
||||
if(lease_ref === null || lease_ref === "")
|
||||
return
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("DELETE", `/-/lease/${lease_ref}`, true);
|
||||
xhr.send();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
'''
|
||||
return HTMLResponse(response)
|
||||
@app.get('/status')
|
||||
async def status(request: Request):
|
||||
return JSONResponse({'status': 'up', 'version': VERSION, 'commit': COMMIT, 'debug': DEBUG})
|
||||
|
||||
|
||||
@app.get('/-/origins')
|
||||
@ -132,12 +89,6 @@ async def _origins(request: Request, leases: bool = False):
|
||||
return JSONResponse(response)
|
||||
|
||||
|
||||
@app.delete('/-/origins')
|
||||
async def _origins_delete(request: Request):
|
||||
Origin.delete(db)
|
||||
return Response(status_code=201)
|
||||
|
||||
|
||||
@app.get('/-/leases')
|
||||
async def _leases(request: Request, origin: bool = False):
|
||||
session = sessionmaker(bind=db)()
|
||||
@ -152,13 +103,6 @@ async def _leases(request: Request, origin: bool = False):
|
||||
return JSONResponse(response)
|
||||
|
||||
|
||||
@app.delete('/-/lease/{lease_ref}')
|
||||
async def _lease_delete(request: Request, lease_ref: str):
|
||||
if Lease.delete(db, lease_ref) == 1:
|
||||
return Response(status_code=201)
|
||||
raise HTTPException(status_code=404, detail='lease not found')
|
||||
|
||||
|
||||
# venv/lib/python3.9/site-packages/nls_core_service_instance/service_instance_token_manager.py
|
||||
@app.get('/client-token')
|
||||
async def client_token():
|
||||
|
54
app/orm.py
54
app/orm.py
@ -1,6 +1,6 @@
|
||||
import datetime
|
||||
|
||||
from sqlalchemy import Column, VARCHAR, CHAR, ForeignKey, DATETIME, update, and_, inspect
|
||||
from sqlalchemy import Column, VARCHAR, CHAR, ForeignKey, DATETIME, UniqueConstraint, update, and_, delete, inspect
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.engine import Engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
@ -43,35 +43,24 @@ class Origin(Base):
|
||||
if entity is None:
|
||||
session.add(origin)
|
||||
else:
|
||||
x = dict(
|
||||
values = dict(
|
||||
hostname=origin.hostname,
|
||||
guest_driver_version=origin.guest_driver_version,
|
||||
os_platform=origin.os_platform,
|
||||
os_version=origin.os_version
|
||||
os_version=origin.os_version,
|
||||
)
|
||||
session.execute(update(Origin).where(Origin.origin_ref == origin.origin_ref).values(**x))
|
||||
session.execute(update(Origin).where(Origin.origin_ref == origin.origin_ref).values(**values))
|
||||
session.commit()
|
||||
session.flush()
|
||||
session.close()
|
||||
|
||||
@staticmethod
|
||||
def delete(engine: Engine, origins: ["Origin"] = None) -> int:
|
||||
session = sessionmaker(bind=engine)()
|
||||
if origins is None:
|
||||
deletions = session.query(Origin).delete()
|
||||
else:
|
||||
deletions = session.query(Origin).filter(Origin.origin_ref in origins).delete()
|
||||
session.commit()
|
||||
session.close()
|
||||
return deletions
|
||||
|
||||
|
||||
class Lease(Base):
|
||||
__tablename__ = "lease"
|
||||
|
||||
origin_ref = Column(CHAR(length=36), ForeignKey(Origin.origin_ref), primary_key=True, nullable=False, index=True) # uuid4
|
||||
lease_ref = Column(CHAR(length=36), primary_key=True, nullable=False, index=True) # uuid4
|
||||
|
||||
origin_ref = Column(CHAR(length=36), ForeignKey(Origin.origin_ref, ondelete='CASCADE'), nullable=False, index=True) # uuid4
|
||||
lease_created = Column(DATETIME(), nullable=False)
|
||||
lease_expires = Column(DATETIME(), nullable=False)
|
||||
lease_updated = Column(DATETIME(), nullable=False)
|
||||
@ -96,14 +85,14 @@ class Lease(Base):
|
||||
@staticmethod
|
||||
def create_or_update(engine: Engine, lease: "Lease"):
|
||||
session = sessionmaker(bind=engine)()
|
||||
entity = session.query(Lease).filter(Lease.lease_ref == lease.lease_ref).first()
|
||||
entity = session.query(Lease).filter(and_(Lease.origin_ref == lease.origin_ref, Lease.lease_ref == lease.lease_ref)).first()
|
||||
if entity is None:
|
||||
if lease.lease_updated is None:
|
||||
lease.lease_updated = lease.lease_created
|
||||
session.add(lease)
|
||||
else:
|
||||
x = dict(origin_ref=lease.origin_ref, lease_expires=lease.lease_expires, lease_updated=lease.lease_updated)
|
||||
session.execute(update(Lease).where(Lease.lease_ref == lease.lease_ref).values(**x))
|
||||
values = dict(lease_expires=lease.lease_expires, lease_updated=lease.lease_updated)
|
||||
session.execute(update(Lease).where(and_(Lease.origin_ref == lease.origin_ref, Lease.lease_ref == lease.lease_ref)).values(**values))
|
||||
session.commit()
|
||||
session.flush()
|
||||
session.close()
|
||||
@ -125,8 +114,8 @@ class Lease(Base):
|
||||
@staticmethod
|
||||
def renew(engine: Engine, lease: "Lease", lease_expires: datetime.datetime, lease_updated: datetime.datetime):
|
||||
session = sessionmaker(bind=engine)()
|
||||
x = dict(lease_expires=lease.lease_expires, lease_updated=lease.lease_updated)
|
||||
session.execute(update(Lease).where(and_(Lease.origin_ref == lease.origin_ref, Lease.lease_ref == lease.lease_ref)).values(**x))
|
||||
values = dict(lease_expires=lease.lease_expires, lease_updated=lease.lease_updated)
|
||||
session.execute(update(Lease).where(and_(Lease.origin_ref == lease.origin_ref, Lease.lease_ref == lease.lease_ref)).values(**values))
|
||||
session.commit()
|
||||
session.close()
|
||||
|
||||
@ -138,14 +127,6 @@ class Lease(Base):
|
||||
session.close()
|
||||
return deletions
|
||||
|
||||
@staticmethod
|
||||
def delete(engine: Engine, lease_ref: str) -> int:
|
||||
session = sessionmaker(bind=engine)()
|
||||
deletions = session.query(Lease).filter(Lease.lease_ref == lease_ref).delete()
|
||||
session.commit()
|
||||
session.close()
|
||||
return deletions
|
||||
|
||||
|
||||
def init(engine: Engine):
|
||||
tables = [Origin, Lease]
|
||||
@ -156,18 +137,3 @@ def init(engine: Engine):
|
||||
session.execute(str(table.create_statement(engine)))
|
||||
session.commit()
|
||||
session.close()
|
||||
|
||||
|
||||
def migrate(engine: Engine):
|
||||
db = inspect(engine)
|
||||
|
||||
def upgrade_1_0_to_1_1():
|
||||
x = db.dialect.get_columns(engine.connect(), Lease.__tablename__)
|
||||
x = next(_ for _ in x if _['name'] == 'origin_ref')
|
||||
if x['primary_key'] > 0:
|
||||
print('Found old database schema with "origin_ref" as primary-key in "lease" table. Dropping table!')
|
||||
print(' Your leases are recreated on next renewal!')
|
||||
print(' If an error message appears on the client, you can ignore it.')
|
||||
Lease.__table__.drop(bind=engine)
|
||||
|
||||
upgrade_1_0_to_1_1()
|
||||
|
Loading…
Reference in New Issue
Block a user