Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update kubernetes config to make use of the ingress #24

Merged
merged 7 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ RUN pip install .[server]
FROM python:${PYTHON_VERSION}-slim as runtime
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
procps \
&& rm -rf /var/lib/apt/lists/*
COPY --from=build /venv/ /venv/
COPY tests/test_data/beamline_parameters.txt tests/test_data/beamline_parameters.txt
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ To work with the GUI you will probably need to run:
module load node
npm install
```

12 changes: 6 additions & 6 deletions gui/config-server-gui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ import {
import * as React from "react";
import { ColorModeSwitcher } from "./ColorModeSwitcher";

var BACKEND = "http://172.23.168.196:8555";
var BACKEND = "https://daq-config.diamond.ac.uk/api";
type FeatureFlag = { name: string; value: boolean };

let start_data = fetch(`${BACKEND}/featurelist/`).then((response) =>
let start_data = fetch(`${BACKEND}/featureflag`).then((response) =>
response.json()
);
var start_data_processed = false;
Expand Down Expand Up @@ -69,7 +69,7 @@ export const App = () => {
function switchFlag(item: string) {
let value = !getFeatureFlagData(item);
fetch(`${BACKEND}/featureflag/${item}?value=${value}`, {
method: "POST",
method: "PUT",
}).then((_) =>
fetch(`${BACKEND}/featureflag/${item}`)
.then((resp) => resp.json())
Expand Down Expand Up @@ -109,7 +109,7 @@ export const App = () => {
fetch(`${BACKEND}/featureflag/${item}`, {
method: "DELETE",
}).then((_) => {
return fetch(`${BACKEND}/featurelist/`)
return fetch(`${BACKEND}/featureflag`)
.then((response) => response.json())
.then((data) => resetDataKeys(data.sort()));
});
Expand Down Expand Up @@ -146,10 +146,10 @@ export const App = () => {
);
}
function createFeatureFlag(item: string) {
fetch(`${BACKEND}/featurelist/${item}`, {
fetch(`${BACKEND}/featureflag/${item}`, {
method: "POST",
}).then((_) => {
return fetch(`${BACKEND}/featurelist/`)
return fetch(`${BACKEND}/featureflag`)
.then((response) => response.json())
.then((data) => resetDataKeys(data.sort()));
});
Expand Down
87 changes: 87 additions & 0 deletions k8s-config-dev.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# This is for test deployment on pollux

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: config-service-db-volume
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 500M
---
apiVersion: v1
kind: Service
metadata:
name: daq-config-server-balance
spec:
type: LoadBalancer
ports:
- name: cs-rest
port: 8555
protocol: TCP
targetPort: 8555
- name: http
port: 8080
protocol: TCP
targetPort: 8080
selector:
app: config-service
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: daq-config-server
spec:
replicas: 1
selector:
matchLabels:
app: config-service
template:
metadata:
labels:
app: config-service
spec:
volumes:
- name: config-service-db-volume
persistentVolumeClaim:
claimName: config-service-db-volume
containers:
- name: config-service-main
image: gcr.io/diamond-privreg/daq-config-server/daq-config-server
imagePullPolicy: Always
command: ["config-service"]
args: ["--dev"]
ports:
- name: cs-rest
containerPort: 8555
protocol: TCP
resources:
limits:
cpu: "1"
memory: 300M
- name: config-service-db
image: gcr.io/diamond-privreg/daq-config-server/daq-config-server-db
command: []
args: ["valkey.conf"]
ports:
volumeMounts:
- mountPath: "/persistent_data/"
name: config-service-db-volume
resources:
limits:
cpu: "1"
memory: 300M
- name: config-service-gui
image: gcr.io/diamond-privreg/daq-config-server/daq-config-server-gui
imagePullPolicy: Always
command: []
ports:
- name: http
containerPort: 8080
protocol: TCP
resources:
limits:
cpu: "1"
memory: 200M
34 changes: 32 additions & 2 deletions k8s-config.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# This is for live deployment on argus

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
Expand All @@ -12,9 +14,9 @@ spec:
apiVersion: v1
kind: Service
metadata:
name: daq-config-server-balance
name: daq-config-server-svc
spec:
type: LoadBalancer
type: ClusterIP
ports:
- name: cs-rest
port: 8555
Expand All @@ -27,6 +29,34 @@ spec:
selector:
app: config-service
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: daq-config-ingress
spec:
ingressClassName: nginx
tls:
- hosts:
- daq-config.diamond.ac.uk
rules:
- host: daq-config.diamond.ac.uk
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: daq-config-server-svc
port:
number: 8080
- path: /api
pathType: Prefix
backend:
service:
name: daq-config-server-svc
port:
number: 8555
---
apiVersion: apps/v1
kind: Deployment
metadata:
Expand Down
5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@ readme = "README.md"
requires-python = ">=3.7"

[project.optional-dependencies]
server = ["fastapi", "redis", "hiredis", "dls-dodal>=1.26.0"]
server = ["fastapi", "redis", "hiredis"]
dev = [
"copier",
"httpx",
"pipdeptree",
"pre-commit",
"pyright",
"pytest",
"pytest-cov",
"pytest-asyncio",
"ruff",
"tox-direct",
"types-mock",
Expand All @@ -51,6 +53,7 @@ reportMissingImports = false # Ignore missing stubs in imported modules
# Run pytest with all our checkers, and don't spam us with massive tracebacks on error
addopts = """
--tb=native -vv
--asyncio-mode=auto
"""
# https://iscinumpy.gitlab.io/post/bound-version-constraints/#watch-for-warnings
filterwarnings = "error"
Expand Down
127 changes: 86 additions & 41 deletions src/config_service/app.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import redis
import uvicorn
from dodal.common.beamlines.beamline_parameters import (
from fastapi import FastAPI, Request, Response, status
from fastapi.middleware.cors import CORSMiddleware
from redis import Redis

from .beamline_parameters import (
BEAMLINE_PARAMETER_PATHS,
GDABeamlineParameters,
)
from fastapi import FastAPI, Response, status
from fastapi.middleware.cors import CORSMiddleware
from .constants import DATABASE_KEYS, ENDPOINTS

from .constants import ENDPOINTS

app = FastAPI()
app = FastAPI(
title="DAQ config service",
description="""For storing and fetching beamline parameters, etc. which are needed
by more than one applicatioon or service""",
root_path="/api",
)
origins = ["*"] # TODO fix this
app.add_middleware(
CORSMiddleware,
Expand All @@ -19,63 +24,99 @@
allow_headers=["*"],
)


valkey = redis.Redis(host="localhost", port=6379, decode_responses=True)
valkey = Redis(host="localhost", port=6379, decode_responses=True)

__all__ = ["main"]

BEAMLINE_PARAM_PATH = ""
BEAMLINE_PARAMS: GDABeamlineParameters | None = None
DEBUG_CATCHALL = False


@app.get(ENDPOINTS.BL_PARAM + "/{param}")
def get_beamline_parameter(param: str):
"""Get a single beamline parameter"""
assert BEAMLINE_PARAMS is not None
return {param: BEAMLINE_PARAMS.params.get(param)}


@app.get(ENDPOINTS.BL_PARAM + "{item_id}")
def beamlineparameter(item_id: str):
@app.get(ENDPOINTS.BL_PARAM)
def get_all_beamline_parameters():
"""Get a dict of all the current beamline parameters."""
assert BEAMLINE_PARAMS is not None
return {item_id: BEAMLINE_PARAMS.params.get(item_id)}
return BEAMLINE_PARAMS.params


@app.post(ENDPOINTS.FEATURE + "{item_id}")
def set_featureflag(item_id: str, value: bool, response: Response):
if not valkey.sismember(ENDPOINTS.FEATURE_LIST, item_id):
@app.get(ENDPOINTS.FEATURE)
def get_feature_flag_list():
"""Get a list of all the current feature flags."""
return valkey.smembers(DATABASE_KEYS.FEATURE_SET)


@app.get(ENDPOINTS.FEATURE + "/{flag_name}")
def get_feature_flag(flag_name: str, response: Response):
"""Get the value of a feature flag"""
if not valkey.sismember(DATABASE_KEYS.FEATURE_SET, flag_name):
response.status_code = status.HTTP_404_NOT_FOUND
return {"message": f"Feature flag {item_id} does not exist!"}
return {"message": f"Feature flag {flag_name} does not exist!"}
else:
return {"success": valkey.set(item_id, int(value))}
ret = int(valkey.get(flag_name)) # type: ignore
return {flag_name: bool(ret) if ret is not None else None, "raw": ret}


@app.post(ENDPOINTS.FEATURE + "/{flag_name}", status_code=status.HTTP_201_CREATED)
def create_feature_flag(flag_name: str, response: Response, value: bool = False):
"""Sets a feature flag, creating it if it doesn't exist. Default to False."""
if valkey.sismember(DATABASE_KEYS.FEATURE_SET, flag_name):
response.status_code = status.HTTP_409_CONFLICT
return {"message": f"Feature flag {flag_name} already exists!"}
else:
valkey.sadd(DATABASE_KEYS.FEATURE_SET, flag_name)
return {"success": valkey.set(flag_name, int(value))}

@app.delete(ENDPOINTS.FEATURE + "{item_id}")
def delete_featureflag(item_id: str, response: Response):
if not valkey.sismember(ENDPOINTS.FEATURE_LIST, item_id):

@app.put(ENDPOINTS.FEATURE + "/{flag_name}")
def set_feature_flag(flag_name: str, value: bool, response: Response):
"""Sets a feature flag, return an error if it doesn't exist."""
if not valkey.sismember(DATABASE_KEYS.FEATURE_SET, flag_name):
response.status_code = status.HTTP_404_NOT_FOUND
return {"message": f"Feature flag {item_id} does not exist!"}
return {"message": f"Feature flag {flag_name} does not exist!"}
else:
valkey.srem(ENDPOINTS.FEATURE_LIST, item_id)
return {"success": not valkey.sismember(ENDPOINTS.FEATURE_LIST, item_id)}
return {"success": valkey.set(flag_name, int(value))}


@app.get(ENDPOINTS.FEATURE + "{item_id}")
def get_featureflag(item_id: str, response: Response):
if not valkey.sismember(ENDPOINTS.FEATURE_LIST, item_id):
@app.delete(ENDPOINTS.FEATURE + "/{flag_name}")
def delete_feature_flag(flag_name: str, response: Response):
"""Delete a feature flag."""
if not valkey.sismember(DATABASE_KEYS.FEATURE_SET, flag_name):
response.status_code = status.HTTP_404_NOT_FOUND
return {"message": f"Feature flag {item_id} does not exist!"}
return {"message": f"Feature flag {flag_name} does not exist!"}
else:
ret = int(valkey.get(item_id)) # type: ignore
return {item_id: bool(ret) if ret is not None else None, "raw": ret}
valkey.srem(DATABASE_KEYS.FEATURE_SET, flag_name)
return {"success": not valkey.sismember(DATABASE_KEYS.FEATURE_SET, flag_name)}


@app.get(ENDPOINTS.FEATURE_LIST)
def get_featureflag_list():
return valkey.smembers(ENDPOINTS.FEATURE_LIST)
@app.get(ENDPOINTS.INFO)
def get_info(request: Request):
"""Get some generic information about the request, mostly for debugging"""
return {
"message": "Welcome to daq-config API.",
"root_path": request.scope.get("root_path"),
"request_headers": request.headers,
}


@app.post(ENDPOINTS.FEATURE_LIST + "{item_id}", status_code=status.HTTP_201_CREATED)
def create_featureflag(item_id: str, response: Response):
if valkey.sismember(ENDPOINTS.FEATURE_LIST, item_id):
response.status_code = status.HTTP_409_CONFLICT
return {"message": f"Feature flag {item_id} already exists!"}
else:
valkey.sadd(ENDPOINTS.FEATURE_LIST, item_id)
return {"success": valkey.set(item_id, 0)}
if DEBUG_CATCHALL:

@app.api_route("/{full_path:path}")
async def catch_all(request: Request, full_path: str):
if DEBUG_CATCHALL:
return {
"message": "resource not found, supplying info for debug",
"root_path": request.scope.get("root_path"),
"path": full_path,
"request_headers": repr(request.headers),
}


def main(args):
Expand All @@ -86,4 +127,8 @@ def main(args):
else:
BEAMLINE_PARAM_PATH = BEAMLINE_PARAMETER_PATHS["i03"]
BEAMLINE_PARAMS = GDABeamlineParameters.from_file(BEAMLINE_PARAM_PATH)
uvicorn.run(app="config_service.app:app", host="0.0.0.0", port=8555)
uvicorn.run(
app="config_service.app:app",
host="0.0.0.0",
port=8555, # root_path="/api"
)
Loading
Loading